Микропроцессорная система управления, предназначенная для использования на лесопильном заводе
p align="left">Адрес первого обработчика составляется из полного слова 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 ; Инициализация переменных системы В системе за некоторыми ячейками памяти закреплена функция хранения переменных. Например, подсчитанный суммарный объем древесины V в двоичном формате хранится в отдельных двух байтах памяти с адресами V_SUM, V_SUM+1. Требуют инициализации только две переменные (инициализируются нулем): V (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,910-3, максимальное - FF,FFН=256,996. Общие правила для всех вычислительных процедур: программист вызывающей программы сам следит, чтобы операнды и результат не выходили за пределы представления и чтобы при вычитании не образовалось отрицательных величин; операнды при сложении, вычитании, умножении, делении хранятся в регистровых парах HL и DE, результат в HL. Процедура косинуса получает операнд и выводит результат в паре HL. Сложение В микропроцессоре Z80 есть команда сложения регистровых пар ADD HL, DE. Однако, чтобы сохранить единообразие, оформим ее все же как подпрограмму. Листинг 8: подпрограмма PLUS ; - - - подпрограмма сложения ; HL+DEHL ; сохраняет A, BC, DE PLUS ADD HL, DE ; RET ; Вычитание В Z80 есть команда вычитания регистровых пар с учетом переноса SBC HL, DE. Используем ее в подпрограмме вычитания, обнулив прежде флаг CY. Листинг 9: подпрограмма MINUS ; - - - подпрограмма сложения ; HL-DEHL ; сохраняет 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 ; - - - подпрограмма умножения ; HLDEHL ; все регистры меняются MUL LD B, H ; BC=1-й множитель LD C, L ; LD HL, 0 ; HL=0 LD A, 16 ; счетчик цикла MUL2 SRL H ; сдвиг 0HLDECY 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-х циклов. В каждом цикле делается сдвиг BCHL0, т.е. делимое HL выдвигается влево в пару BC. Затем BC сравнивается с DE (путем вычитания BC-DE и проверки флага переноса). Если BC>DE, то младший разряд HL устанавливается единицей, в противном случае он остается нулем. Это один из разрядов частного. После 16 сдвигов в делимое HL полностью сдвинется влево и на его место придет частное целочисленного деления, в BC будет остаток деления, DE сохранит делитель. На этом этапе целая часть частного в HL заносится в стек, а HL обнуляется. В оставшихся 8-ми сдвигах BCHL в BC будут сдвигаться только нули. После всех 24-х циклов в регистре L будет дробная часть частного, а в стеке целая часть. Листинг 11: подпрограмма DIV ; - - - подпрограмма деления ; HL/DEHL ; сохраняет DE, использует стек (2 байта) DIV LD BC, 0 ; LD A, 24 ; А - счетчик циклов DIV4 PUSH AF ; сохранить счетчик А в стеке ADD HL, HL ; BCHL0 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 ; Косинус Чтобы вычислить косинус, используется ограниченный четырьмя членами ряд Тейлора: , угол изменяется от 0 до /2. (6) Эта формула дает косинус с максимальной погрешностью 8,910-4, это меньше, чем ошибка разрядности используемого формата [1].[1] дробных чисел. (7) Представление (7) формулы (6) гораздо удобнее для вычисления косинуса программно. Достаточно сделать процедуру для вычисления , где n - целое число, - дробное число. Перед обращением к этой процедуре (названной COS_A) операнды содержатся: HL = 2; DE = ; BC = n в формате n.0 (B=n, C=0). Результат - в HL Эта подпрограмма использует предыдущие арифметические программы. Листинг 12: подпрограмма COS_A ; - - - вспомогательная подпрограмма для косинуса ; операнды: HL, DE, BC ; изменяются все регистры COS_A PUSH BC ; сохранить в стеке n перед вызовом MUL CALL MUL ; HL=HLDE 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=2 PUSH HL ; 2 в стеке LD D, 1 ; DE=1.0 LD E, 0 ; LD B, #1E ; BC=1E.0H=30 LD C, 0 ; CALL COS_A ; HL=1-2/30 EX HL, DE ; POP HL ; HL=2 PUSH HL ; 2 в стеке LD B, #0C ; BC=C.0H=12 LD C, 0 ; CALL COS_A ; HL=1-2/12(1-2/30) EX HL, DE ; POP HL ; HL=2 LD B, #02 ; BC=2.0 LD C, 0 ; CALL COS_A ; HL=1-2/2[1-2/12(1-2/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 ; CYC (получаем последний бит операнда) 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. Она использует равенство: 10HL=2HL+8HL, а умножения на 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 ; HL10=HL2+HL8 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 ; рассчитать адрес очередного элемента массива
Страницы: 1, 2, 3, 4
|