Если кто помнит еще 51-й АСМ и найдет минуточку взглянуть, буду очень благодарен.
Вот такая программка (в сокращении). Должна в течение 10 секунд посчитать импульсы с компаратора (число оборотов ветрогенератора), а потом выдать результат по запросу в интерфейсе SPI:
;- векторы прерываний (использованы только два)
;--------------------------------------------------
org 0000H
int00: jmp strt ;прерывание по Reset
org 0003H
int01: jmp VYVOD ;прерывание INT0
;--------------------------------- начало программы
org 0032H
strt: MOV SP, #07H ; установка стека (не очень-то нужно, там вроде и так 07Н по-умолчанию)
SETB EX0 ; Разрешить прерывание по входу INT0
SETB PX0 ; Установить для входа INT0 высший приоритет (не очень нужно, он и так единственный)
SETB TCON.0 ; Установить режим прерывания по срезу с единицы в ноль
SETB EA ; Разрешить прерывания
;------------------------------------ Инициализируем регистры и порты
MOV R0, #0 ; На всякий случай обнулим регистры
MOV R1, #0
MOV R2, #0
MOV R3, #0
MOV R4, #0
MOV P1, #255 ; Порт 1 переключен на чтение
MOV P0, #0
MOV P2, #0
;----------------------------------- Начинаем счет импульсов
NACH: MOV R0, #0 ; Просто с потолка... будем считать, что на выходе компаратора в прошлый раз был ноль.
SETB P1.7 ; Это я зажег контрольный светодиод, который показывает, что счет идет
; ------------------------- Здесь идут аж три вложенных цикла, потому что ни в один ни в два такое время не укладывается
MOV R5, #74 ; Числа подобраны экспериментально для времени тройного цикла - 10 секунд
N2: MOV R6, #255 ; Числа подобраны экспериментально для времени тройного цикла - 10 секунд
N1: MOV R7, #50 ; Числа подобраны экспериментально для времени тройного цикла - 10 секунд
STCN: JB P1.0, STC1 ;Проверяем первый бит порта компаратора (входной сигнал с ветряка)
;----------------------------------------
;... строки удалены
;... этот алгоритм проверялся сто раз
;... и работает без претензий
;----------------------------------------
STC3: DJNZ R7, STCN ; После чего начинаем следующую итерацию счета во внутреннем цикле
NOP ; Добавлены НОП-ы для заверщения полного цикла за 10 секунд
NOP
NOP
NOP
NOP
DJNZ R6, N1 ;Следующая итерация счета во втором цикле
DJNZ R5, N2 ;Следующая итерация счета во внешнем цикле
; ---------------------- Когда все три вложенных цикла пройдены (прошли 10 секунд)
MOV ACC, R1 ; Запоминаем резульат в R3 и R4 на случай прихода прерывания
MOV R3, ACC
MOV ACC, R2 ; (вот такой ассемблер, блин, прямо из регистра в регистр низзя)
MOV R4, ACC
CLR P1.7 ; Это я погасил контрольный светодиод, который показывал, что счет идет
CALL FLS ; Ждем некоторое время, чтобы прочитать число на контрольных светодиодах
;-----------------
; снова строки удалены, это просто задержка
;-----------------
MOV R1, #0 ; Обнуляем счетные регистры для нового счета
MOV R2, #0
JMP NACH ; Повторяем цикл счета
;--------------------------------- Программа обработки прерывания
; Нам нужно передать на выход последовательно слово 16 бит, сохраненное в регистрах R3 и R4.
VYVOD:CLR EA ; Запрещаем прерывания
CLR EX0
CLR IE.7 ; Это я погасил контрольный светодиод, который показывал, что счет идет
PUSH PSW ; Сохраняем статус в стеке
PUSH ACC
PUSH B
MOV ACC, R0 ; Сразу регистры в стек тоже низзя
PUSH ACC
MOV ACC, R1
PUSH ACC
MOV ACC, R2
PUSH ACC
MOV ACC, R5
PUSH ACC
MOV ACC, R6
PUSH ACC
MOV ACC, R7
PUSH ACC
;---------------------------------------------------
MOV A, R4 ; Забираем из памяти старший байт результата
; Передаем бит 15
CH15: JNB P1.1, CH15 ;ждем единицу на клоке
MOV P3, A ;выводим аккумулятор в порт и читаем аппаратно старший бит порта (номер 7)
RL A ;сдвигаем аккумулятор влево на единичку
; Передаем бит 14
CH14: JB P1.1, CH14 ;если единица еще не ушла - ждем нуля
CL14: JNB P1.1, CL14 ;если на клоке уже 0 - ждем единицу
MOV P3, A ;выводим аккумулятор в порт и читаем аппаратно старший бит порта (номер 7)
RL A ;сдвигаем аккумулятор влево на единичку
; Биты 13 - 9 передаются аналогично
; Передаем бит 8
CH8: JB P1.1, CH8 ;если единица еще не ушла - ждем нуля
CL8: JNB P1.1, CL8 ;если на клоке уже 0 - ждем единицу
MOV P3, A ;выводим аккумулятор в порт и читаем аппаратно старший бит порта (номер 7)
MOV A, R3 ; А тут вместо сдвижки забираем из памяти младший байт
; Биты 7 - 1 передаются аналогично 13 - 9
; Передаем бит 0
CH0: JB P1.1, CH0 ;если единица еще не ушла - ждем нуля
CL0: JNB P1.1, CL0 ;если на клоке уже 0 - ждем единицу
MOV P3, A ;выводим аккумулятор в порт и читаем аппаратно старший бит порта (номер 7)
; ------------------------- Больше сдвигать ничего не нужно, 16 бит переданы
;---------------------------------------------------
POP ACC ; Возвращаем статус из стека
MOV R7, ACC
POP ACC
MOV R6, ACC
POP ACC
MOV R5, ACC
POP ACC
MOV R2, ACC
POP ACC
MOV R1, ACC
POP ACC
MOV R0, ACC
POP B
POP ACC
POP PSW
SETB P1.7 ; Это я зажег контрольный светодиод, который показывает, что счет продолжается
SETB EA ; Разрешаем прерывания
RETI ; Возврат из прерывания
FLS: (задержка на 0.2 сек по традиционной схеме с декрементом регистров R6 и R7. Кончается оператором RET.)
END
---------------------
А в чем вопрос? Эта красивая программа, получив перепад с единицы в ноль на вход INT0, начинает отрабатывать подпрограмму VYVOD, как и должна бы. И даже что-то на выход идет. Проконтролировать точность вывода не могу, осциллограф не синхронизируется, а микросекунды промелькивают на экране очень быстро.
Впрочем, во время отладки я выделял ее как законченную программу исполняемую в цикле и убедился, что вывод байтов поисходит абсолютно правильно.
А беда в том, что когда подпрограмма заканчивается, возврат в программу не происходит. Что при этом происходит - непонятно. Просто все висит.
Хотел прокатать эту программу пошагово в Протеусе (до этого Протеусом не пользовался, но поскольку его в форуме так хвалят, почему бы и нет). Поставил Протеус, сляпал в нем схему, загрузил в проект программу, все скомпилировалось и заработало. Точно так же, как в железе. Со всеми зависаниями. Но, блин, как в нем посмотреть на пошаговое исполнение программы... не нашел. От попытки не отказался, надо все же этот софт освоить, он явно полезный. Но пока не понимаю, как увидеть окно с пошаговым исполнением инструкций.
Но может быть у меня в программе какой-то явный ляп, которого я просто не вижу и для которого и Протеус не нужен. Определенно же дело или в адресах возврата или неправильно использовано прерывание. В общем, взгляните, ели не лень будет. Сам знаю, в чужой программе разбираться тоскливо. (Конечно особенно приятно будет, если кто-то не ошибку найдет, а научит "рыбу ловить", то бишь, объяснит, как в Протеусе запустить пошаговое исполнение, чтобы было видно, какие строки в данный момент исполняются).
Только пожалуйста, не надо советов, что считать импульсы надо было счетчиками, а не программой, или подобных. Сам знаю, что надо, но "исторически" так сложилось. И теперь меня раздражает, что с элементарным прерыванием не справился.
Полный текст программы на всякий случай прилагаю.