Пробую обрабатывать на контроллере прирывания по таймеру и по порту B (interrupt on change). Суть проблемы в том, что почему-то нажатие на одной из кнопки обрабатывается не корректно. При генерации прерывания на порту RB5 должен меняться уровень порта RA1, но вместе с этим два раза меняется уровень порта RA0, хотя не должен. Код программы ниже. Скриншот схемы в приложении. Сам проект в MPLAB X и ISIS в архиве в приложении, в нем же видео демонстрирующее проблему.
Суть программы в том, что таймер настроен на 1мс (1024мкс) и прерывание обработки таймера дикриментирует переменную COUNTER и если она равна 0, меняет уровень светодиода на порту RA3, таким образом он моргает примерно раз в четверть секунды. Уровни светодиодов RA0 и RA1 меняются только по нажатию на кнопки.
Раздел: МК для начинающих
Суть программы в том, что таймер настроен на 1мс (1024мкс) и прерывание обработки таймера дикриментирует переменную COUNTER и если она равна 0, меняет уровень светодиода на порту RA3, таким образом он моргает примерно раз в четверть секунды. Уровни светодиодов RA0 и RA1 меняются только по нажатию на кнопки.
list p=16f84a
#include "P16F84A.INC"
__config _CP_OFF & _PWRTE_OFF & _WDT_OFF & _HS_OSC
_work equ h'4E'
_status equ h'4F'
COUNTER equ h'0C' ; Счетчик
; Вектор сброса контроллера ----------------------------------------------------
org 000 ; При старте контроллера (адрес программы 000)
goto MAIN ; перейти к метке MAIN
; Вектор прерывания ------------------------------------------------------------
org 004 ; При возникновении прерывания (переход к адресу 004)
goto IRQ_PROCESS ; перейти к метке IRQ_PROCESS
; Фоновая программа начинается с инициализации ---------------------------------
MAIN
bsf INTCON, GIE ; Разрешаем прерывания
bsf INTCON, RBIE ; Разрешаем прерывания на портах RB7:RB4
bsf STATUS, RP0 ; Выбираем первый банк памяти
bcf OPTION_REG, T0CS ; Включаем timer0 в режим таймера, а не счетчика
bcf OPTION_REG, PSA ; Связываем предделитель с таймером
bcf OPTION_REG, PS2 ; Конфигурируем предделитель в соответствии
bcf OPTION_REG, PS1 ; с даташитом в 1:4
bsf OPTION_REG, PS0 ; (Биты PS2:PS0 установлены в 001)
movlw b'00000000' ; Конфигурируем все пины порта А
movwf TRISA ; как выходы
movlw b'00110000' ; Порты RB5:RB4 как входы
movwf TRISB
bcf STATUS, RP0 ; Возвращаемся к нулевому банку памяти
bsf INTCON, T0IE ; Включаем прерывание по таймеру
movlw b'00000' ; Выставляем низкий уровень на всех
movwf PORTA ; пинах порта А
; Бесконечный пустой цикл
M_LOOP
nop ; Ничего не делаем
goto M_LOOP
IRQ_PROCESS
; Сохраняем контекст
movwf _work ; Сохраняем рабочий регистр в памяти данных
swapf STATUS, w ; Считываем регистр STATUS в рабочий регистр не меняя флагов
movwf _status ; И сохраняем его из рабочего регистра в память данных
btfsc INTCON, T0IF ; Если прерывание по таймеру
call IRQ_TMR0 ; вызываем обработчик таймера
btfsc INTCON, RBIF ; Если прерывание по порту B
call IRQ_RB ; вызываем обработчик порта B
; Чтобы сбросить бит RBIF обязательно надо вначале прочитать порт B
; Сбрасывать прерывание на порту B надо всегда, оно постоянно почему-то срабатывает
movf PORTB, w ; Сбрасываем флаг внешнего прерывания, чтобы
bcf INTCON, RBIF ; не получился бесконечный цикл
; Восстанавливаем контекст
swapf _status, w ; Сначала восстанавливаем
movwf STATUS ; регистр STATUS
swapf _work, f ; Затем рабочий регистр
swapf _work, w ; не воздействуя на флаги регистра STATUS
retfie ; И возврат из прерывания в программу
IRQ_TMR0
decf COUNTER
btfsc STATUS, Z
call COUNT_OVER
bcf INTCON, T0IF
return
COUNT_OVER
movf PORTA, w ; Считываем порт А в рабочий регистр
xorlw b'01000' ; и по маске изменяем нужный пин
movwf PORTA ; Записываем изменения в порт А
movlw h'FF' ; Инициализируем счетчик
movwf COUNTER
return
IRQ_RB
; В зависимости от порта, на котором возникло прерывания, вызываем
; соответствующую подпрограмму (проверяем по низкому уровню, т.е. нажатие кнопки)
btfss PORTB, RB4
call IRQ_RB4
btfss PORTB, RB5
call IRQ_RB5
IRQ_RB4
movf PORTA, w ; Считываем порт А в рабочий регистр
xorlw b'00001' ; и по маске изменяем нужный пин
movwf PORTA ; Записываем изменения в порт А
return
IRQ_RB5
movf PORTA, w
xorlw b'00010'
movwf PORTA
return
end
Прикрепленные файлы:
Раздел: МК для начинающих