Микропроцессорная система управления, предназначенная для использования на лесопильном заводе

то ошибка ПЗУ

… ; иначе тест ПЗУ успешен, продолжаем тесты


Подпрограмма E_ROM выводит на два самых левых индикатора условный код ошибки ПЗУ (“E2”), а остальные индикаторы гасит. Индикация такой ошибки говорит о необходимости проверки и перепрограммирования микросхемы ПЗУ.


Листинг 4: процедура E_ROM, вызываемая после ошибки ПЗУ

; – – – обработка ошибки ПЗУ (код “E2”)

E_ROM LD A, #79 ; A¬“E”

OUT (#0A), A ;

LD A, E ; A¬код ошибки (2)

OUT (#09), A ;

XOR A ; A¬“0”

LD C, #08 ;

LD D, #18 ;

LD B, 6 ; счетчик

E_ROM1 OUT (C), A ; последние шесть индикаторов гасим

OUT (D), A ;

DEC C ;

DEC D ;

DJNZ E_ROM1 ;

JR E_ROM ;

Тест фотоэлементов

Перед началом работы нужно проверить прохождение света от ламп к фотоэлементам. Если один из фотоприемников не чувствует света, это значит, что или перегорела лампа фотоэлемента, или в момент пуска бревно уже лежит между фотоэлементами и его длина не может быть измерена, или повредился канал от фотоэлемента к процессору. В обоих случаях микропроцессорная система не начнет свою работу (т.к. нет возможности отличить второй случай от остальных). При обнаружении такой ошибки на индикаторы выводится признак ошибки фотоэлемента в виде условного кода “E3”.

В программе этого теста читается порт ввода статуса фотоэлементов (адрес 0B) и проверяются на ноль два его младших бита. Если хотя бы один не равен нулю, то происходит переход на обработчик ошибки. Причем для обработки ошибки можно использовать ту же процедуру E_ROM, перед вызовом загрузив в регистр E код ошибки 3 (экономия памяти).


Листинг 5: тест фотоэлементов

; – – – проверка двух младших битов порта ФЭЛ на 0

TST_FEL IN A, (#0B) ; прочесть слово статуса ФЭЛ

AND 3 ; наложить на него маску 000000112

LD E, 3 ;

JP NZ, E_ROM ; если не ноль, то ошибка

… ; иначе продолжаем тесты


Инициализация программируемого контроллера прерываний

Для инициализации контроллера надо переслать ему два управляющих слова ICW1 и ICW2, первое по адресу 0CН (A0=0), второе – по адресу 0DН (A0=1).

Пересылаются следующие управляющие слова:

Рис. 6 Применяемые управляющие слова инициализации ПКП

Слово ICW1 установит одиночный (без каскадного соединения) режим работы ПКП (бит 1), 4-хбайтный интервал для начальных адресов обработчиков прерываний (бит 2).

Биты 5–7 слова ICW1 вместе со всем словом ICW2 сообщат контроллеру, что первый обработчик (запроса IR0) начинается с адреса 0020Н.

Контроллер накладывает определенные ограничения на расположение обработчиков в памяти. Первое из них в том, подпрограммы обработки прерываний должны располагаться по порядку, начиная с адреса обработчика запроса IR0, и с постоянным интервалом, т.е. образовывать в памяти таблицу. Интервал расположения может составлять 4 байта (если бит 2 ICW1 равен единице, как в нашем случае), или 8 байт (если бит 2 равен нулю). При постоянном интервале адреса всех обработчиков определятся расположением первого (IR0).

Адрес первого обработчика составляется из полного слова ICW2 (старший байт) и битов 7,6,5 слова ICW1 (старшие три бита младшего байта адреса).

В нашем случае старший байт равен 00Н, младший байт равен 0010 0000 = 20Н. Обработчики имеют начальные адреса: 0020H, 0024H, 0028H, 002CH, 0030H, 0034H (6 обработчиков).

Процедура INI_PIC инициализирует контроллер.


Листинг 6: инициализация программируемого контроллера прерываний

; – – – переслать 38H в порт 0CH и 0 в порт 0DH

INI_PIC LD A, #38 ;

OUT (#0C), A ;

XOR A ;

OUT (#0D), A ;


Инициализация переменных системы

В системе за некоторыми ячейками памяти закреплена функция хранения переменных. Например, подсчитанный суммарный объем древесины VS в двоичном формате хранится в отдельных двух байтах памяти с адресами V_SUM, V_SUM+1.

Требуют инициализации только две переменные (инициализируются нулем):

VS (2 байта) – начальный адрес V_SUM;

Время (4 байта) – начальный адрес TIME.

Ясно, что эти имена (V_SUM и TIME) лишь условные обозначения (также, как, например, имена меток в листингах программ). При переводе в машинный код эти имена транслируются в двухбайтный адрес.


Листинг 7: инициализация ячеек суммарного объема и времени

; – – – начальное обнуление объема и времени

INI_VAR XOR A ;

LD (V_SUM), A ;

LD (V_SUM+1), A ;

LD (V_SUM+2), A ;

LD (TIME), A ;

LD (TIME+1), A ;

LD (TIME+2), A ;

LD (TIME+3), A ;

… ; следует продолжение переходит к основному циклу работы (описание см. в п.4.4).

Арифметические подпрограммы

Микропроцессорной системе необходимо “уметь” выполнять арифметические операции: сложение, вычитание, умножение, деление и косинус. В этом же разделе приведем и неарифметические процедуры для осуществления индикации: преобразования данных в двоично-десятичный код и в семисегментный.

Основным форматом чисел в МП системе является двухбайтный формат с фиксированной точкой вида 1байт , 1байт . Формат беззнаковый, предполагается, что числа положительны. Одно число умещается в одной регистровой паре. Минимальное представимое число – 0,01H=1/256=3,9Ч10-3, максимальное – FF,FFН=256,996.

Общие правила для всех вычислительных процедур:

программист вызывающей программы сам следит, чтобы операнды и результат не выходили за пределы представления и чтобы при вычитании не образовалось отрицательных величин;

операнды при сложении, вычитании, умножении, делении хранятся в регистровых парах HL и DE, результат в HL. Процедура косинуса получает операнд и выводит результат в паре HL.


Сложение

В микропроцессоре Z80 есть команда сложения регистровых пар ADD HL, DE. Однако, чтобы сохранить единообразие, оформим ее все же как подпрограмму.


Листинг 8: подпрограмма PLUS

; – – – подпрограмма сложения

; HL+DE®HL

; сохраняет A, BC, DE

PLUS ADD HL, DE ;

RET ;

Вычитание

В Z80 есть команда вычитания регистровых пар с учетом переноса SBC HL, DE. Используем ее в подпрограмме вычитания, обнулив прежде флаг CY.


Листинг 9: подпрограмма MINUS

; – – – подпрограмма сложения

; HL–DE®HL

; сохраняет A, BC, DE

MINUS OR A ; A не меняется, но флаг CY=0

SBC HL, DE ;

RET ;


Умножение

При умножении первый множитель хранится в паре BC (скопируем его туда из HL в начале), второй множитель хранится в DE. Результат первоначально получается в четырехбайтном виде HL.DE, затем “обрезается” до H.L.

Результат произведения накапливается в HLDE. В течение 16 циклов сдвигов HLDE второй множитель DE постепенно выдвигаясь, уходит вправо и на его место приходит из HL готовые биты произведения. Они получаются при суммировании первого множителя (BC) и левой половины накопленной суммы HLDE (HL). Суммирование делается в случае, если перед этим при сдвиге HLDE вправо из DE был выдвинут единичный бит.


Листинг 10: подпрограмма MUL

; – – – подпрограмма умножения

; HLґDE®HL

; все регистры меняются

MUL LD B, H ; BC=1-й множитель

LD C, L ;

LD HL, 0 ; HL=0

LD A, 16 ; счетчик цикла

MUL2 SRL H ; сдвиг 0®HLDE®CY

RR L ;

RR D ;

RR E ;

JR NC, MUL1 ; если выдвинут 0, то на конец цикла

ADD HL, BC ; если выдвинута 1, то сложить

MUL1 DEC A ;

JR NZ, MUL2 ;

LD H, L ; получено произведение HL.DE

LD L, D ; которое “обрезаем” до L.D и переносим в H.L

RET


Деление

При делении делимое хранится в HL, делитель в DE. Результат получается в виде трех байт [стек.L] (где “стек” – содержимое вершины стека), в конце программы младший байт стека переносится в H и окончательный результат имеет формат H.L. Регистровая пара BC в начале обнуляется.

Деление производится в течение 24-х циклов. В каждом цикле делается сдвиг BC¬HL¬0, т.е. делимое HL выдвигается влево в пару BC. Затем BC сравнивается с DE (путем вычитания BC–DE и проверки флага переноса). Если BC>DE, то младший разряд HL устанавливается единицей, в противном случае он остается нулем. Это один из разрядов частного.

После 16 сдвигов в делимое HL полностью сдвинется влево и на его место придет частное целочисленного деления, в BC будет остаток деления, DE сохранит делитель. На этом этапе целая часть частного в HL заносится в стек, а HL обнуляется. В оставшихся 8-ми сдвигах BC¬HL в BC будут сдвигаться только нули. После всех 24-х циклов в регистре L будет дробная часть частного, а в стеке целая часть.


Листинг 11: подпрограмма DIV

; – – – подпрограмма деления

; HL/DE®HL

; сохраняет DE, использует стек (2 байта)

DIV LD BC, 0 ;

LD A, 24 ; А – счетчик циклов

DIV4 PUSH AF ; сохранить счетчик А в стеке

ADD HL, HL ; BC¬HL¬0

RL C ;

RL B ;


LD A, C ;

SUB E ;

LD C, A ;

LD A, B ;

SBC D ;

LD B, A ;

JR NC, DIV1 ; если BC>DE, то переход


LD A, C ;

ADD E ;

LD C, A ;

LD A, B ;

ADC D ;

LD B, A ;

JR DIV2 ;


DIV1 INC HL ; если BC>DE: установить последний бит

; частного в 1

DIV2 POP AF ; извлечь счетчик А из стека

CP #09 ; проверить, 16-й цикл идет или нет

JR NZ, DIV3 ;

PUSH HL ; если 16-й цикл, то в HL целая часть частного,

; сохранить ее в стеке, чтобы извлечь

; в конце программы

LD HL, 0 ;

DIV3 DEC A ;

JR NZ, DIV4 ; конец цикла

POP BC ; извлекаем из стека целую часть частного

LD H, C ; получаем частное в H.L

RET ;


Косинус

Чтобы вычислить косинус, используется ограниченный четырьмя членами ряд Тейлора:


, угол a изменяется от 0 до p/2. (6)


Эта формула дает косинус с максимальной погрешностью 8,9Ч10-4, это меньше, чем ошибка разрядности используемого формата [1].[1] дробных чисел.


(7)

Представление (7) формулы (6) гораздо удобнее для вычисления косинуса программно. Достаточно сделать процедуру для вычисления , где n – целое число, b – дробное число. Перед обращением к этой процедуре (названной COS_A) операнды содержатся:

HL = a2;

DE = b;

BC = n в формате n.0 (B=n, C=0).

Результат – в HL

Эта подпрограмма использует предыдущие арифметические программы.


Листинг 12: подпрограмма COS_A

; – – – вспомогательная подпрограмма для косинуса

; операнды: HL, DE, BC

; изменяются все регистры

COS_A PUSH BC ; сохранить в стеке n перед вызовом MUL

CALL MUL ; HL=HLґDE

POP DE ; DE=n

CALL DIV ; HL=HL/DE

LD D, 1 ; DE=1.0

LD E, 0 ;

EX HL, DE ;

CALL MINUS ; HL=HL-DE

RET ;


Подпрограмма косинуса вычисляет косинус угла в паре HL.

Листинг 13: подпрограмма COS

; – – –подпрограмма косинуса

; операнд и результат в HL

; изменяются все регистры

COS LD D, H ;

LD E, L ;

CALL MUL ; HL=a2

PUSH HL ; a2 в стеке

LD D, 1 ; DE=1.0

LD E, 0 ;

LD B, #1E ; BC=1E.0H=30

LD C, 0 ;

CALL COS_A ; HL=1-a2/30

EX HL, DE ;

POP HL ; HL=a2

PUSH HL ; a2 в стеке

LD B, #0C ; BC=C.0H=12

LD C, 0 ;

CALL COS_A ; HL=1-a2/12(1-a2/30)

EX HL, DE ;

POP HL ; HL=a2

LD B, #02 ; BC=2.0

LD C, 0 ;

CALL COS_A ; HL=1-a2/2[1-a2/12(1-a2/30)]

RET ;

Преобразование двоичный®двоично-десятичный код

В управляющей программе надо переводить двухбайтные числа формата [12].[34] в двоично-десятичный код вида [123.45] размером в пять тетрад для последующего перевода в семисегментный код и индикации.

Для этого нужны две отдельных подпрограммы: одна (по имени B2D) для перевода целой части числа [12] и вторая (имя B2D_F) для перевода дробной части числа [34].

Подпрограмма B2D переводит целое двоичное число в регистре C (0..FFH) в 2-10 код, расположенный в регистровой паре HL. Перевод производится в соответствии с формулой:


HL=(…((c7)Ч2+c6)Ч2+…+c1)Ч2+c0,


в которой ci – разряды числа в регистре C, а удвоение и сложение с битами ci происходит по правилам десятичной арифметики (с командой DAA после операции).


Листинг 14: подпрограмма B2D

; – – – перевод байта (целого) в 2-10 код

; операнд C – переводимое число, результат в HL

; сохраняет DE

B2D LD B, 8 ;

B2D1 SLA C ; CY¬C (получаем последний бит операнда)

LD A, L ; удвоение HL с учетом переноса CY

ADC L ; по правилам десятичной арифметики

DAA ;

LD L, A ;

LD A, H ;

ADC H ;

DAA ;

LD H, A ;

DJNZ B2D1 ; конец цикла

RET ;


Подпрограмма B2D_F переводит дробное число в формате 0.L в 2-10 код из трех тетрад в формате 0.ABC (учитываются три цифры после запятой). Регистры A, B, C содержат в конце каждый по одной десятичной цифре. Перевод происходит так. Число 0.L умножается на 10, результат в паре H.L. Его целая часть (H) и будет первой цифрой A результата. Затем H обнуляется, полученная дробь 0.L снова умножается на 10 и т.д.

Для быстрого умножения на 10 сделана отдельная подпрограмма MUL10, умножающая пару HL (где H=0) и получающая результат в той же HL. Она использует равенство:

10ЧHL=2ЧHL+8ЧHL, а умножения на 2 и на 8 делаются с помощью команды ADD HL, HL.


Листинг 15: подпрограммы B2D_F и MUL10

; – – – перевод байта (дробного) в 2-10 код

; операнд C – число с фиксированной перед старшим разрядом точкой, результат в ABC

; изменяются все регистры

B2D_F LD H, 0 ;

CALL MUL10 ; получить в H первую цифру

LD A, H ; скопировать ее в A

LD H, 0 ; и обнулить H

CALL MUL10 ;

LD B, H ; вторую цифру – в регистр B

LD H, 0 ;

CALL MUL10 ; третью – в C

LD C, H ;

RET

; – – – умножение на 10

; операнд в HL (имеет значение только L) и результат в HL

; сохраняет A, BC

MUL10 ADD HL, HL ; HLЧ10=HLЧ2+HLЧ8

LD D, H ;

LD E, L ;

ADD HL, HL ;

ADD HL, HL ;

ADD HL, DE ;

RET ;


Преобразование двоичный®семисегментный код

Это преобразование с помощью таблицы перекодировки уже встречалось в тестовой программе ОЗУ. Сейчас оформим ее как отдельную подпрограмму (в тесте ОЗУ нельзя вызывать подпрограммы, т.к. команда вызова CALL использует стек). Операнд подпрограммы – двоично-десятичная цифра в регистре A (0..9, старшая тетрада нулевая).


Листинг 16: подпрограмма D27

; – – – перевод байта в семисегментный код

; операнд (0..9) и результат в A

; сохраняет BC, DE

D27 LD H, #07 ;

LD L, A ;

LD A, (HL) ;

RET ;

Обработчики прерываний

Всего их шесть – по числу прерываний. Обработчик обязательно должен сохранить в стеке все изменяемые им регистры и в конце восстановить их. Выход из обработчика выполняется не стандартным RET, а командой RETI.


Обработчик IRQ0 (начало измерений)

Функция обработчика – обнулить таймеры T1 и T2, а также специальную ячейку памяти D_NUM (2 байта). Эта ячейка инкрементируется всякий раз после чтения напряжения Ud с датчика диаметра. По приходу запроса IRQ0, когда приходит новое бревно, она должна быть обнулена.


Листинг 17: обработчик запроса IRQ0

; – – – обнулить T1, T2, D_NUM

IR0_H PUSH BC ;

PUSH AF ;

LD BC, 0 ;

LD (T1), BC ; обнулить T1

LD (T2), BC ; обнулить T2

LD (D_NUM), BC ; обнулить D_NUM

POP AF ;

POP BC ;

RETI ;


Обработчик IRQ2 (информация с АЦП готова)

Функция обработчика – если бревно сейчас под пластиной датчика (можно судить, прочтя порт фотоэлементов с адресом 0B), считать два байта напряжения из портов 00 (младший) и 01 (старший). Записать их в массив напряжений, под который отведена область памяти начиная с адреса 0100Н до конца ОЗУ (всего 2К). Перед записью проверить, не заполнен ли этот массив. Инкрементировать ячейку D_NUM, содержащую число элементов этого массива.


Листинг 18: обработчик запроса IRQ2

; – – – считать и обработать байт с АЦП

IR2_H PUSH BC ;

PUSH HL ;

PUSH AF ;


IN A, (#0B) ;

AND #02 ; наложить маску 000000010

JR Z, IR2_H1 ; если второй бит нулевой, то выход

LD HL, #1000 ; рассчитать адрес очередного элемента массива

LD C, (D_NUM) ; считать D_NUM

LD B, (D_NUM+1) ;

SLA C ; умножить его на 2

RL B ;

ADD HL, BC ; теперь адрес в HL

LD A, H ;

CP #18 ;

JR NC, IR2_H1 ; если вышли за пределы массива, то выход

IN A, (#00) ; считать первый байт с АЦП

LD (HL), A ; и отправить его в память

INC HL ;

IN A, (#01) ; считать второй

LD (HL), A ; отправить

INC BC ; увеличить переменную D_NUM на единицу

LD (D_NUM), BC ;

IR2_H1 POP AF ;

POP HL ;

POP BC ;

RETI ;


Обработчик IRQ3 (от генератора 16 Гц)

Функция обработчика – произвести инкремент часов реального времени и условный инкремент таймеров. Часы реального времени – это 4 байта в памяти:

TIME 1/16 секунды (0..15);

TIME+1 секунды (0..59);

TIME+2 минуты (0..59);

TIME+3 часы (0..23);

Все величины хранятся в двоичном формате.

Таймерам T1 и T2 отведено по 2 байта с начальными адресами T1 и T2. Условия, при которых они инкрементируются, были приведены в п. 3.4.


Листинг 19: обработчик запроса IRQ3

; – – – инкремент часов реального времени и условный инкремент таймеров

IR3_H PUSH BC ;

PUSH HL ;

PUSH AF ;

; часы реального времени

LD HL, TIME ;

INC (HL) ; инкремент 1/16 секунд

LD A, (HL) ;

CP 16 ; проверить на достижение максимума JR C, IR3_H1 ; условный выход из подпрограммы

LD (HL), 0 ; иначе обнулить 1/16 секунды и продолжить

INC HL ;

LD B, 2 ; инкремент секунд и минут делается в цикле

IR3_H2 INC (HL) ;

LD A, (HL) ;

CP 60 ;

JR C, IR3_H1 ;

LD (HL), 0 ;

INC HL ;

DJNZ IR3_H2 ; конец цикла

INC (HL) ; инкремент часов

LD A, (HL) ;

CP 24 ;

JR C, IR3_H1 ;

XOR A ; если счетчик часов=24

LD (HL), A ; то обнулить все 4 байта часов реального времени

DEC HL ;

LD (HL), A ;

DEC HL ;

LD (HL), A ;

DEC HL ;

LD (HL), A ;

; таймеры

IR3_H1 IN A, (#0B) ; загрузить слово статуса фотоэлементов

AND 1 ;

JR Z, IR3_H3 ; если не установлен 1-й бит, то выход

LD HL, T2 ; иначе инкремент Т2

INC (HL) ;

JR NZ, IR3_H4 ; если инкремент не обнулил

; первый байт Т2, то идем дальше

INC HL ; иначе увеличить на 1 и второй байт

INC (HL) ;

IR3_H4 IN A, (#0B) ;

AND 2 ; проверить 2-й бит статуса ФЭЛ

JR Z, IR3_H3 ; если он не установлен, то выход

LD HL, T1 ; иначе инкремент Т1

INC (HL) ;

JR NZ, IR3_H3 ;

INC HL ;

INC (HL) ;

IR3_H3 POP AF ;

POP HL ;

POP BC ;

RETI ;


Обработчик IRQ4 (от кнопки “+Час”)

Функция обработчика – увеличить на единицу часы реального времени (ячейка TIME+3).


Листинг 20: обработчик запроса IRQ4

; – – – инкремент часов

IR4_H PUSH HL ;

PUSH AF ;

LD HL, TIME+3;

INC (HL) ; инкремент часов

LD A, (HL) ;

CP 24 ;

JR C, IR4_H1 ;

XOR A ; если счетчик часов=24

LD (HL), A ; то обнулить часы и минуты

DEC HL ;

LD (HL), A ;

IR4_H1 POP AF ;

POP HL ;

RETI


Обработчик IRQ5 (от кнопки “+Мин”)

Функция обработчика – увеличить на единицу минуты реального времени (ячейка TIME+2).


Листинг 21: обработчик запроса IRQ5

; – – – инкремент минут

IR5_H PUSH HL ;

PUSH AF ;

LD HL, TIME+2;

INC (HL) ; инкремент минут

LD A, (HL) ;

CP 60 ;

JR C, IR5_H1 ;

XOR A ; если счетчик минут=60

LD (HL), A ; то обнуление минут

INC HL ; и инкремент часов

INC (HL) ;

LD A, (HL) ; с проверкой часов на 24

CP 24 ;

JR C, IR5_H1 ;

XOR A ; если счетчик часов=24

LD (HL), A ; то обнулить и часы

IR4_H1 POP AF ;

POP HL ;

RETI


Обработчик IRQ1 (от фотоэлемента Фэл2)

Обработчик IRQ1 выполняет самую важную функцию. Его задача – вычислить объем бревна. Последовательность следующая: вычисляем диаметр бревна, длину, вычисляем объем Vi, находим объем VS.

Для вычисления диаметра все значения, прежде считанные в массив напряжений с АЦП, усредняются: суммируются и делятся на количество (D_NUM). При суммировании может произойти переполнение суммы (а она двухбайтная), чтобы этого не было, массив разбивается на группы по 16 измерений в каждой. Если осталась остаточная группа с числом меньше 16, то она отбрасывается. В каждой из них подсчитывается среднее, затем рассчитывается искомое как среднее средних.

Из среднего напряжения находится угол


a=.


Затем находим диаметр d=0,625 – 0,5cos a=00,A0H – 00,80Hcos a. Занести его в ячейку DIAM (2 байта в памяти). Сравнить диаметр с допустимыми пределами [0,2..0,5]=[0,33H..0,8H]. Если он выходит за эти пределы, то выдать на отбраковку (порт 02H) единицу.

Объем Vi находится как Vi=(p/4)d2ЧT1/T2=0,C9HЧd2ЧT1/T2.


Листинг 22: обработчик запроса IRQ1

; – – – найти объем бревна и суммарный объем

; усреднение всех напряжений с датчика диаметра в массиве по адресу 1000H

IR1_H PUSH AF ;

PUSH BC ;

PUSH DE ;

PUSH HL ;


LD L, (D_NUM) ;

LD H, (D_NUM+1) ;

LD B, 4 ; делим D_NUM на 16

IR1_H1 SRL H ;

RR L ;

DJNZ IR1_H1 ;

LD C, L ; в результате C=число групп по 16

PUSH BC ; сохранить С в стеке


LD HL, #1000 ;

LD DE, 0 ; DE – начальная сумма групп

PUSH DE ; отправить ее в стек, C станет второй в стеке

PUSH DE ; DE – начальная сумма отдельной группы,

; отправить®в стек, сумма групп вторая в стеке

; C – третья в стеке

IR1_H4 LD B, 16 ;

IR1_H2 LD E, (HL) ; читаем в DE элемент массива

INC HL ;

LD D, (HL) ;

INC HL ;

EX (SP), HL ; текущую сумма в HL, текущий адрес в стеке

ADD HL, DE ;

EX (SP), HL ; новая сумма в стеке, текущий адрес в HL

DJNZ IR1_H2 ;

; в итоге сумма одной группы по 16 – в стеке

; начальный адрес следующей группы – в HL

POP DE ;

LD B, 4 ; находим среднее одной группы,

IR1_H3 SRL D ; деля сумму в DE на 16

RR E ;

DJNZ IR1_H3 ;

POP HL ; берем из стека сумму групп

ADD HL, DE ;

PUSH HL ; снова отправляем в стек: сначала сумму групп

PUSH DE ; затем сумму одной группы

DEC C ; С – счетчик групп

JR NZ, IR1_H4 ; следующая группа…


POP HL ;

POP HL ; извлечь найденную сумму групп

POP BC ; извлечь счетчик групп C

LD D, C ; DE=C.0

LD E, 0 ;

CALL DIV ; делим сумму групп на число групп

; теперь HL=Ud=среднее всего массива напряжений датчика

; следующий шаг – нахождение угла a, cos a, d

LD D, 4 ; DE=491H

LD E, #91 ;

LD B, H ; сохранить Ud в BC

LD C, L ;

EX DE, HL ;

CALL MINUS ; HL=491H-Ud

EX HL, DE ;

LD H, B ;

LD L, C ;

CALL DIV ; HL= Ud/(491H-Ud)

LD D, 0 ;

LD E, #4F ;

CALL MUL ; HL=a

CALL COS ; HL=cos a

LD D, 0 ;

LD E, #80 ;

CALL MUL ; HL=0,5cos a

LD D, 0 ;

LD E, #A0 ;

EX HL, DE ;

CALL MINUS ; HL=0,625-0,5cos a=d

LD (DIAM), HL ; занести диаметр в память

; формируем сигнал отбраковки

LD A, L ;

CP #33 ;

JR NC, IR1_H5 ; если d>20, то идем дальше

LD A, 1 ; иначе в порт отбраковки записать 1

OUT (#02), A ;

JR IR1_OUT ; и выход, не считая объем

IR1_H5 LD A, L ;

CP #80 ;

JR C, IR1_H6 ; если d<50, то идем дальше

LD A, 1 ; иначе в порт отбраковки записать 1

OUT (#02), A ;

; дальше находим Vi, VS

IR1_H6 LD L, (T1) ;

LD H, (T1+1) ;

LD E, (T2) ;

LD D, (T2+1) ;

CALL DIV ; HL=T1/T2

LD E, (DIAM) ;

LD D, (DIAM+1) ;

PUSH DE ;

CALL MUL ; HL=T1Чd/T2

POP DE ;

CALL MUL ; HL=T1Чd2/T2

LD D, 0 ;

LD E, #C9 ;

CALL MUL ; HL=(p/4)T1Чd2/T2=Vi

EX HL, DE ;

LD L, (V_SUM) ;

LD H, (V_SUM+1) ;

CALL PLUS ; HL=VS

LD (V_SUM), HL ; занести суммарный объем в память


IR1_OUT POP HL ;

POP DE ;

POP BC ;

POP AF ;

RETI ;


Основная исполняемая часть программы

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

Ее функции:

прочесть VS из ячейки V_SUM, перевести его в 2-10 код (подпрограммы B2D, B2D_F), перевести каждую цифру в семисегментный код (подпрограмма D27) и вывести в порт индикаторов (03-0A), присоединив десятичную запятую в индикаторе 06Н;

прочесть время из ячейки TIME (TIME+1–секунды, TIME+2–минуты, TIME+3–часы), перевести его в 2-10 код, перевести каждую цифру в семисегментный код и вывести в порт индикаторов (13-1A), присоединив десятичную запятую в индикаторах 13Н, 15Н, 17Н.


Рис. 7 Расположение объема и времени на индикаторах


Листинг 23: основная часть

; – – – вывести на индикаторы объем и время

; вывод объема

MAIN LD E, (V_SUM) ;

LD D, (V_SUM+1) ; D.E – суммарный объем

; выводим целую часть D

LD C, D ;

CALL B2D ; HL=2-10 код D

LD A, H ;

CALL D27 ; в семисегментный код

OUT (#08), A ; вывести в порт

LD A, H ;

AND #0F ;

CALL D27 ;

OR #80 ; примешать десятичную запятую

OUT (#06), A ; вывести в порт

LD A, H ;

RR A ; четырежды сдвигаем вправо

RR A ;

RR A ;

RR A ;

AND #0F ;

CALL D27 ;

OUT (#07), A ; вывести в порт

; выводим дробную часть E

LD L, E ;

CALL B2D_F ; ABC=три цифры 2-10 кода числа 0.L

CALL D27 ;

OUT (#05), A ; вывести в порт

LD A, B ;

CALL D27 ;

OUT (#04), A ; вывести в порт

LD A, C ;

CALL D27 ;

OUT (#03), A ; вывести в порт

; вывод текущего времени

LD HL, (TIME+1) ; HL=адрес секунд

LD B, #13 ; самый правый индикатор

MAIN1 PUSH HL ;

LD A, (HL) ;

LD C, A ;

CALL B2D ; HL=2-10 код секунд/минут/часов (причем Н=0)

LD A, L ;

AND #0F ;

CALL D27 ;

OR #80 ; примешать запятую

OUT (B), A ; вывести в порт

INC B ; следующий индикатор

LD A, L ;

RR A ; четырежды сдвигаем А вправо

RR A ;

RR A ;

RR A ;

AND #0F ;

CALL D27 ;

OUT (B), A ; вывести в порт

INC B ; следующий индикатор


POP HL ;

INC HL ; перейти к следующей ячейке (TIME+2, TIME+3)

LD A, B ;

CP #08 ; проверка конца цикла

JR NZ, MAIN1 ;


JP MAIN ; начинаем все сначала


Общая структура ПЗУ микропроцессорной системы

Наконец, приведем карты ПЗУ и ОЗУ микропроцессорной системы.

ПЗУ


0000 DI
0001

Начало тестов

TST_RAM

JR 0038

0020

Распределитель обработчиков прерываний

0020 JP IR_H0

0024 JP IR_H1

0028 JP IR_H2

002C JP IR_H3

0030 JP IR_H4

0034 JP IR_H5

0038

Продолжение теста ОЗУ (TST_RAM)

TST_ROM

TST_FEL

INI_PIC

INI_VAR


EI

LD SP, #0FFF

Главный цикл программы

MAIN


Подпрограммы

E_RAM

E_ROM

PLUS

MINUS

MUL

DIV

COS_A

COS

B2D

B2D_F

MUL10

D27


Обработчики

IR_H0

IR_H1

IR_H2

IR_H3

IR_H4

IR_H5


Область нулей 00000000
0700 Таблица перекодировки
070F 00000000
07FF Последний байт (дополнение контрольной суммы до нуля)

Общая структура ОЗУ микропроцессорной системы. ОЗУ

0800 V_SUM
0802 DIAM
0804 TIME
0808 T1
080A T2
080C D_NUM
080D
0FFF

­ ОБЛАСТЬ СТЕКА ­

1000

Массив напряжений

с датчика диаметра

(2К)

1FFF Конец ОЗУ

Заключение


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

ПЕРЕЧЕНЬ ЭЛЕМЕНТОВ


Поз. обозн. Наименование Кол. Примечание
DD1 Микросхема Z-80 1
DD2, DD4 Микросхема К155ИР13 2
DD3, DD5 Микросхема К537РУ8 2
DD6 Микросхема К1118ПВ1 1
DD7 Микросхема К546ИР5 1
DD8 Микросхема К155ИД3 1
DD9 Микросхема К541РЕ1 1
DD10 Микросхема К580ВН59А 1
DD11 Микросхема К155ЛД7 1
Rd Резистор МЛТ-5.1К 1
R1, R3 Резистор МЛТ-10К 4
R 2, Rдоб Резистор МЛТ-1К 3
R9, R10

Резистор МЛТ-1-13 кОм А

ТУ ОЖО 467 003

2
R11, R12

Резистор МЛТ-1-510 кОм А

ТУ ОЖО 467 003

2