У нашій мережі будуть функціонувати чотири датчика температури На вигляд вони невиразні Їх ідентифікаційні коди1 заховані всередині і прочитати їх можна лише програмним шляхом Можна, звичайно, скористатися командою Дослідження ПЗУ для ідентифікації всіх датчиків мережі Але при цьому залишається неясним, якому саме фізичній датчику відповідає даний код Ми поступимо іншим способом Перед установкою датчиків на кабель мережі протестуємо кожен з них командою Читання ПЗУ. Для цього, по черзі підключаючи датчики до одного з майстрів, прочитаємо його ПЗУ за допомогою цієї команди

Нижче представлений лістинг програми на бейсике, яка виконує єдину функцію: читає ПЗУ підключеного до шини датчика У цій програмі передбачається, що датчик підключається до біту 0 першому каналу інтерфейсу і що використовується зовнішнє живлення датчика

Лістинг 1 Програма читання ПЗУ окремого датчика

‘Оголошуємо процедури

DECLARE SUB ReadROM (Address%, Bit%)

DECLARE FUNCTION SearchForPresence% (Address%, Bit%)

DECLARE SUB Delay ()

‘Оголошуємо змінні

DIM SHARED FirstAddress AS INTEGER

DIM SHARED SecondAddress AS INTEGER

DIM SHARED ThirdAddress AS INTEGER DIM SHARED FourthAddress AS INTEGER DIM SHARED StatusAddress AS INTEGER

DIM SHARED ReadR0MArray (7) AS INTEGER Масив, що містить біти

‘Команди 33h

‘Визначаємо чисельні значення констант і змінних

FirstAddress = &ampH360

SecondAddress = FirstAddress + 1

ThirdAddress = FirstAddress + 2

FourthAddress = FirstAddress + 3

StatusAddress = FirstAddress + 4

‘Заповнюємо масив команди Читання ПЗУ

A% = &ampH33

FOR I% = 7 TO 0 STEP -1

ReadR0MArray(I%) = A% \ 2 Л I%

A% = A% MOD 2 a I%

NEXT I%

‘Запускаємо цикл читання ПЗУ T = TIMER

DO UNTIL TIMER T > 1 Встановлюємо час тайм-ауту рівним 1 сек A% = SearchForPresence (FirstAddress, 0) Перевіряємо наявність

‘Датчика

SELECT CASE A%

CASE 0 Датчик підключений

CALL ReadR0M (FirstAddress, 0) Викликаємо процедуру читання

‘ПЗУ

CASE 1 Датчик не підключений

PRINT На лінії немає датчиків температури

END SELECT

IF INKEY $ = CHR $ (27) THEN EXIT DO Забезпечуємо вихід по

‘Клавіші Esc

LOOP

END

SUB Delay Підпрограма, що формує тривалість тимчасового слота

FOR I% = 1 TO 40: NEXT I%

END SUB

SUB ReadROM (Address%, Bit%) Підпрограма читання ПЗУ DS1820 DO

‘Очищаємо строкову змінну для вмісту ПЗУ S $ = “

‘Проводимо ініціалізацію DO

A% = SearchForPresence(Address%, Bit%)

SELECT CASE A%

CASE 0 EXIT DO CASE ELSE EXIT SUB END SELECT LOOP

‘Видаємо команду Читання ПЗУ (33h)

FOR I% = 0 TO 7

SELECT CASE ReadROMArray(I%)

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Надсилаємо 64 тимчасових слота читання і прочитання біти складаємо в строкову змінну S $

FOR U% = 0 TO 63 OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0

A% = (INP(Address%) AND 2 Л Bit%) / 2 л Bit%

S$ = S$ + RTRIM$(LTRIM$(STR$(A%)))

NEXT U%

‘Обчислюємо ЦВК

‘Визначаємо 8-бітовий зсувний масив (регістр)

DIM Register(7) AS INTEGER

‘Обнуляємо цей масив

FOR I% = 0 TO 7: Register(I%) = 0: NEXT I%

‘Починаючи з молодшого розряду, побитово обробляємо дані з допомогою функції

‘XOR і зсувного регістру Проходимо всі біти, крім старших восьми У результаті в регістрі (масиві) виявиться ЦВК

N% = LEN(S$) 8 FOR I% = 1 TO N%

C% = VAL(MID$(S$, I%, 1))

A% = Register(0) XOR C%

B% = Register(3) XOR A%

C% = Register(4) XOR A%

Register(O) = Register(1)

Register(1) = Register(2)

Register(2) = B%

Register(3) = C%

Register(4) = Register(5)

Register(5) = Register(6)

Register(6) = Register(7)

Register(7) = A%

NEXT l%

‘Перетворимо числове значення регістра в строкове G $ = “

FOR l% = O TO 7

G$ = G$ + RTRlM$(LTRlM$(STR$(Register(l%))))

NEXT l%

‘Порівнюємо отриману рядок зі старшими 8-у бітами рядка даних Якщо ці рядки збігаються, то дані прийняті вірно lF G $ = RlGHT $ (S $, 8) THEN EXlT DO ELSE

EXlT SUB END lF LOOP

‘Виводимо вміст ПЗУ на екран (для зручності спочатку однією рядком, а потім в стовпчик побайтово)

CLS

LOCATE 1, 1: PRlNT S$

LOCATE 3, 1 FOR l% = O TO 7

PRlNT MlD$(S$, 1 + l% * 8, 8)

NEXT l%

SLEEP END SUB

FUNCTlON SearchForPresence% (Address%, Bit%)

‘Функція перевірки присутності на лінії хоча б одного датчика температури

‘Чекаємо поки шина перейде у високе стан OUT StatusAddress, O DO

A% = lNP(Address%) AND 2 Л Bit%

LOOP WHlLE A% = O Скидаємо потрібний біт OUT Address%, 255 2 л Bit%

OUT StatusAddress, 1

‘Затримка на довжину імпульсу скидання

FOR I% = 1 TO 10: Delay: NEXT I%

‘Звільняємо шину

OUT StatusAddress, 0

‘Затримка на відновлення шини

Delay

‘Читаємо імпульс присутності SearchForPresence = INP (Address%) AND 2 Л Bit%

‘Чекаємо поки шина перейде у високе стан OUT StatusAddress, 0 DO

A% = INP(Address%) AND 2 л Bit%

LOOP WHILE A% = 0 END FUNCTION

З приводу цього лістингу слід зробити пару зауважень По-перше, щоб не ускладнювати картину, ми всі тимчасові затримки оформили у вигляді порожніх циклів Це найпростіший спосіб створення в програмі тимчасових затримок, але не самий хороший При використанні порожніх циклів тривалість затримки цілком залежить від апаратного забезпечення компютера На різних компютерах вищенаведена програма буде давати різні затримки Може скластися ситуація, коли вона взагалі не буде працювати Для її правильного функціонування число порожніх циклів для затримок потрібно вибирати, виходячи з швидкодії конкретного компютера На практиці це робиться шляхом підбору Можна, наприклад, ввести в програму процедуру, яка, спираючись на внутрішній таймер, визначить, скільки часу займе, наприклад, виконання 1000000 порожніх циклів Розділивши потім це число циклів на число мікросекунд минулого тимчасового інтервалу, можна отримати число порожніх циклів, що припадають на 1 мікросекунду Цим числом можна потім керуватися, вводячи програмні затримки, необхідні однопровідним протоколом обміну Однак, і тут жодної гарантії точності відліку часових інтервалів немає Справа в тому, що, залежно від конкретних завдань, що вирішуються в даний момент процесором, число порожніх циклів в одиницю часу буде різним Тим не менше, наші дослідження показали, що така організація тимчасових інтервалів для цілей однопровідного протоколу дає надійні результати Для більш грамотного визначення тимчасових затримок можна, наприклад, піти шляхом перепрограмування мікросхеми таймера Друге зауваження з приводу вищенаведеного лістингу полягає в тому, що в ньому не відображені процедури збереження отриманого значення ПЗУ в файлі у вигляді, зручному для подальшого використання Програма також гідно не оптимізована за кодом і часу роботи Вона служить виключно для ілюстрації процесу читання ПЗУ

Нарешті, наведемо лістинг програми, що забезпечує опитування чотирьох датчиків нашої мережі (рис 64), висновок значень температури на екран монітора, а також висновок кожні чверть години значень температури в файл з привязкою до реальних часом і датою Іншими словами, ця програма дозволяє здійснити постійний моніторинг температури чотирьох точок обєкта і зафіксувати ці температури у файлі

Лістинг 2 Програма опитування чотирьох датчиків температури

‘Оголошуємо процедури

DECLARE SUB ConvertAndReadTemperature (Address%, A$, N%, M%, T$) DECLARE FUNCTION SearchForPresence% (Address%, Bit%)

DECLARE SUB FillCommandArrays ()

DECLARE SUB Delay ()

‘Оголошуємо змінні DIM SHARED FirstAddress AS INTEGER DIM SHARED SecondAddress AS INTEGER DIM SHARED ThirdAddress AS INTEGER DIM SHARED FourthAddress AS INTEGER DIM SHARED StatusAddress AS INTEGER

DIM SHARED ConvertTemperatureArray (7) AS INTEGER Масив, що містить біти команди 44h

DIM SHARED MatchROMArray (7) AS INTEGER Масив, що містить біти команди 55h

DIM SHARED ReadScratchpadArray (7) AS INTEGER Масив, що містить біти команди BEh

DIM SHARED UniqueCodesArray (3) AS STRING * 64 Масив, що містить коди ПЗУ підключених датчиків

DIM SHARED Temperature (3) AS SINGLE Масив для зберігання значень температур

DIM SHARED ErrorFlag AS INTEGER Прапор факту помилкового читання

‘Підключаємо файли, що містять оголошення функцій форматування часу $ INCLUDE: datimbi

‘$INCLUDE: formatbi

‘Визначаємо чисельні значення констант і змінних

CONST TRUE = -1

CONST FALSE = 0

FirstAddress = &ampH360

SecondAddress = FirstAddress + 1

ThirdAddress = FirstAddress + 2

FourthAddress = FirstAddress + 3

StatusAddress = FirstAddress + 4

‘Викликаємо підпрограму заповнення масивів команд FillCommandArrays

‘Заповнюємо вмістом ПЗУ підключених датчиків відповідний масив

UniqueCodesArray(0) = &quot000010001000110110100000100111000000000000000000000000000100 0011&quot

UniqueCodesArray(1) = &quot000010001001111111011010100111000000000000000000000000001000 0100&quot

UniqueCodesArray(2) = &quot000010000011011000000010100111000000000000000000000000001110 1110&quot

UniqueCodesArray(3) = &quot000010001100100010011000100111000000000000000000000000001011 0101&quot

‘Робочий цикл опитування датчиків і фіксації їх показань CLS

LOCATE 1, 1: PRINT Температура першого датчика =;

LOCATE 2, 1: PRINT Температура другого датчика =;

LOCATE 3, 1: PRINT Температура третього датчика =;

LOCATE 4, 1: PRINT Температура четвертого датчика =;

DO

ErrorFlag = 0 T$ = TIME$

FOR U% = 0 TO 3 SELECT CASE U%

CASE 0, 2

CALL ConvertAndReadTemperature(FirstAddress, UniqueCodesArray(U%), U%, 0, T$)

CASE 1, 3

CALL ConvertAndReadTemperature(FirstAddress, UniqueCodesArray(U%), U%, 1, T$)

END SELECT NEXT U%

IF ErrorFlag = 0 THEN

LOCATE 1, 34: PRINT USING &quot+####" Temperature(0)

LOCATE 2, 34: PRINT USING &quot+####" Temperature(l)

LOCATE 3, 34: PRINT USING &quot+####" Temperature(2)

LOCATE 4, 34: PRINT USING &quot+####" Temperature(3)

END IF

SELECT CASE MID $ (T $, 4, 5) Кожні чверть години пишемо

‘Свідчення в файл CASE 00:00, 15:00, 30:00, 45:00

SELECT CASE ErrorFlag CASE 0 A# = Now#

D$ = FormatD$(A#, &quotdd-mm-yyyy&quot)

T$ = FormatD$(A#, &quothh:mm:ss&quot)

X$ = D$ + &quot_&quot + T$

FileNumber = FREEFILE

FileName$ = MID$(D$, 7, 4) + MID$(D$, 4, 2) + MID$(D$, 1,

2) + &quot.dat&quot

OPEN FileName$ FOR APPEND AS FileNumber PRINT #FileNumber, TAB(0) A# TAB(19) X$

FOR I% = 0 TO 3

PRINT #FileNumber, TAB(39 + I% * 7) USING &quot+####"

Temperature(I%)

NEXT I%

CLOSE FileNumber BEEP

DO UNTIL MID$(TIME$, 7, 2) <> &quot00&quot: LOOP CASE ELSE END SELECT CASE ELSE END SELECT

IF INKEY$ = CHR$(27) THEN EXIT DO LOOP END

SUB ConvertAndReadTemperature (Address%, ROMCode$, Unit%, Bit%,

T$)

‘Підпрограма видачі завдань датчикам на перетворення температури і знімання інформації з них

Temperature (Unit%) = 0 Проводимо ініціалізацію DO

A% = SearchForPresence(Address%, Bit%)

SELECT CASE A%

CASE 0 EXIT DO CASE ELSE EXIT SUB END SELECT

‘Видаємо команду Вибір ПЗУ (55h)

FOR I% = 0 TO 7

SELECT CASE MatchROMArray(I%)

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Видаємо 64-бітовий код потрібного датчика FOR I% = 1 TO 64

SELECT CASE VAL(MID$(ROMCode$, I%, 1))

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Записуємо в DS1820 команду 44h (Перетворення температури) FOR I% = 0 TO 7

SELECT CASE ConvertTemperatureArray(I%)

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Чекаємо 500 мс поки відбувається перетворення SELECT CASE T $

CASE &quot24:00:00&quot

T = 0: DO UNTIL TIMER T > 5: LOOP CASE ELSE

T = TIMER: DO UNTIL TIMER T > 5: LOOP END SELECT

‘Проводимо ініціалізацію DO

A% = SearchForPresence(Address%, Bit%) SELECT CASE A%

CASE 0 EXIT DO CASE ELSE EXIT SUB END SELECT LOOP

‘Видаємо команду Вибір ПЗУ (55h)

FOR I% = 0 TO 7

SELECT CASE MatchROMArray(I%)

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Видаємо 64-бітовий код потрібного датчика FOR I% = 1 TO 64

SELECT CASE VAL (MID $ (ROMCode $, I%, 1)) CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay

END SELECT NEXT I%

‘Записуємо команду BEh (Читання СОП) в DS1820 FOR I% = 0 TO 7

SELECT CASE ReadScratchpadArray(I%)

CASE 0 Записуємо 0

OUT Address%, 0 OUT StatusAddress, 1 Delay

OUT StatusAddress, 0 CASE 1 Записуємо 1

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0 Delay END SELECT NEXT I%

‘Читаємо девять байтів повної СОП датчика в строкову змінну S $ S $ = “

FOR U% = 0 TO 71

OUT Address%, 0 OUT StatusAddress, 1 OUT StatusAddress, 0

A% = (INP(Address%) AND 2 Л Bit%) / 2 л Bit%

S$ = S$ + RTRIM$(LTRIM$(STR$(A%)))

NEXT U%

‘Обчислюємо ЦВК

‘Визначаємо 8-бітовий зсувний масив (регістр)

DIM Register(7) AS INTEGER

‘Обнуляємо цей масив

FOR I% = 0 TO 7: Register(I%) = 0: NEXT I%

‘Починаючи з молодшого розряду, побитово обробляємо дані з допомогою функції XOR і зсувного регістру Проходимо всі біти, крім старших восьми У результаті в регістрі (масиві) виявиться ЦВК

N% = LEN(S$) 8 FOR I% = 1 TO N%

C% = VAL(MID$(S$, I%, 1))

A% = Register(0) XOR C%

B% = Register(3) XOR A%

C% = Register(4) XOR A%

Register(0) = Register(1)

Register(1) = Register(2)

Register(2) = B%

Register(3) = C%

Register(4) = Register(5)

Register(5) = Register(6)

Register(6) = Register(7)

Register(7) = A%

NEXT I%

‘Перетворимо числове значення регістра в строкове G $ = “

FOR I% = 0 TO 7

G$ = G$ + RTRIM$(LTRIM$(STR$(Register(I%))))

NEXT I%

‘Порівнюємо отриману рядок зі старшими 8-у бітами рядка даних Якщо ці рядки збігаються, то дані прийняті вірно IF G $ = RIGHT $ (S $, 8) THEN A% = TRUE ELSE

A% = FALSE ErrorFlag = 1 EXIT SUB END IF

‘Обчислюємо значення температури SELECT CASE A%

CASE TRUE

L$ = MID$(S$, 1, 8)

H$ = MID$(S$, 9, 8)

T% = 0

SELECT CASE INSTR (H $, 1) Якщо в старшому байті 1, то T ° < 0 CASE 0

FOR I% = 8 TO 1 STEP -1

T% = T% + VAL(MID$(L$, I%, 1)) * 2 Л (I% 1)

NEXT I%

Temperature(Unit%) = T% / 2 CASE ELSE

FOR I% = 8 TO 1 STEP -1

T% = T% + VAL(MID$(L$, I%, 1)) * 2 л (I% 1)

NEXT I%

Temperature(Unit%) = (T% 256) / 2 END SELECT CASE FALSE END SELECT END SUB

SUB Delay Підпрограма тимчасової затримки (задає тривалість тимчасового слота)

FOR I% = 1 TO 40: NEXT I%

END SUB

SUB FillCommandArrays Підпрограма заповнення масивів,

‘Містять коди команд

‘Заповнюємо масив команди Вибір ПЗУ (55h)

A% = &ampH55

FOR I% = 7 TO 0 STEP -1

MatchROMArray(I%) = A% \ 2 Л I%

A% = A% MOD 2 a I%

NEXT I%

‘Заповнюємо масив команди Перетворення температури (44h) A% = & H44

FOR I% = 7 TO 0 STEP -1

ConvertTemperatureArray(I%) = A% \ 2 Л I%

A% = A% MOD 2 a I%

NEXT I%

‘Заповнюємо масив команди Читання СОП (BEh)

A% = &ampHBE

FOR I% = 7 TO 0 STEP -1

ReadScratchpadArray(I%) = A% \ 2 Л I%

A% = A% MOD 2 a I%

NEXT I%

END SUB

FUNCTION SearchForPresence% (Address%, Bit%)

‘Функція перевірки присутності на лінії хоча б одного датчика температури

‘Чекаємо поки шина перейде у високе стан OUT StatusAddress, 0 DO

A% = INP(Address%) AND 2 Л Bit%

LOOP WHILE A% = 0 Скидаємо потрібний біт OUT Address%, 255 2 л Bit%

OUT StatusAddress, 1

‘Затримка на довжину імпульсу скидання

FOR I% = 1 TO 10: Delay: NEXT I%

‘Звільняємо шину

OUT StatusAddress, 0

‘Затримка на відновлення шини

Delay

‘Читаємо імпульс присутності SearchForPresence = INP (Address%) AND 2 Л Bit%

‘Чекаємо поки шина перейде у високе стан OUT StatusAddress, 0 DO

A% = INP(Address%) AND 2 л Bit% LOOP WHILE A% = 0 END FUNCTION

Джерело: карнач АС, Белошенко ВА, Тітієвський ВІ, Мікролокальние мережі: інтелектуальні датчики, Однопровідна інтерфейс, системи збору інформації Донецьк: ДонФТІ НАНУ України, 2000 199с з іл