Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: PIC16F74 непонятки с прерыванием
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры > PIC
ddd-ekb
Здравствуйте.
Столкнулся с непонятным явлением при обработке прерывания по переполнению таймера1.
Исходные данные:
сделаны часы на PIC16F74, используется статическая индикация и контроллер выбран просто из-за большого количества выводов
быстродействие не требуется абсолютно, поэтому установлен LP режим и кварц на 32768
при данной частоте и использовании предделителя, период переполнения таймера1 составляет 64 сек
поэтому данный таймер использован для подсчёта минутных интервалов
разрешено единственное прерывание по его переполнению, в процедуре обработки сначала старший байт таймера перезагружается значением 10Н (чтоб сократить период до 60 сек), затем увеличивается регистр минут, часов, напоследок сбрасывается флаг прерывания
основная программа просто непрерывно по кругу выводит часы и минуты в сегментном коде на порты и проверяет кнопки управления

Проблема такая:
в сутки хаотично добавляется от 5 до 20 минут
все попытки как то программно отследить происходящее эффекта не дали
рассматривался и вопрос ложных прерываний от другово источника (в обработчик вставлялась проверка флагов и вывод на индикацию если будет обнаруже отличный от переполнения таймера), и помехи по кнопкам управления (они просто выкидывались из исходного кода)
нет, происходит именно хаотичное двойное увеличение счетчика при обработке, такое впечатление что иногда не сбрасывается флаг и после выполнения RETFIE прерывание тут же вызывается снова

Проблема в принципе решена:
в итоге прерывание было просто запрещено, в обработчике RETFIE заменено на обычный RETURN, в начало обработчика вставлена проверка бита переполнения и выход если бит не обнаружен, во все циклы основной программы просто натыканы CALL 4
т.е. реализовано тоже самое прерывание, только тупо программной имитацией
все сбои сразу исчезли

Вопрос:
куда копать то?
задача текущая конечно закрыта, но надо на будущее как то понять всё же sm.gif
ViKo
Цитата(ddd-ekb @ Oct 28 2015, 18:13) *
нет, происходит именно хаотичное двойное увеличение счетчика при обработке, такое впечатление что иногда не сбрасывается флаг и после выполнения RETFIE прерывание тут же вызывается снова

Так попробуйте сбрасывать флаг сразу при входе в прерывание. Что вы теряете?
Herz
Цитата(ViKo @ Oct 28 2015, 17:58) *
Так попробуйте сбрасывать флаг сразу при входе в прерывание. Что вы теряете?

+1. Более того, если этого не делать, то повторный вызов практически гарантирован, имхо. Даже странно другое: что это не всегда происходит. Если мы верно поняли проблему автора.
ddd-ekb
Цитата(Herz @ Oct 28 2015, 21:09) *
если этого не делать, то повторный вызов практически гарантирован, имхо

хмммм.........., я вас понял, проверю
но я не понял какая разница за сколько команд до RETFIE сбрасывать флаг?
главное сбросить до
ну а если обаботчик, допустим, вообще состоял бы из 2-3 коменд?
допустим по прерыванию просто сбросить бит в каком-либо порту и всё
это как то некорректно и обработчик должен быть не менее, допустим, 15 команд?
бррррр................
есть какие то ссылки на аппноты по данной теме?
Код
    org    4

    movwf    buf1
    movf    STATUS,w
    movwf    buf2
    clrf    STATUS

    movlw    16
    movwf    TMR1H

    incf    min,f
    movlw    60
    subwf    min,w
    btfss    STATUS,C
    goto    iend

    clrf    min
    incf    hour,f
    movlw    24
    subwf    hour,w
    btfsc    STATUS,C
    clrf    hour
end
    clrf    PIR1
    movf    buf2,w
    movwf    STATUS
    movf    buf1,w
    retfie
evc
Цитата(ddd-ekb @ Oct 28 2015, 19:35) *
Код
    movlw    16
    movwf    TMR1H

Каждый раз когда перезаписываете один из регистров Таймера1, вы обнуляете предделитель.
Цитата(ddd-ekb @ Oct 28 2015, 19:35) *
goto iend
...
end
clrf PIR1
movf buf2,w
movwf STATUS
movf buf1,w
retfie

???
ddd-ekb
Цитата(evc @ Oct 28 2015, 23:58) *
Каждый раз когда перезаписываете один из регистров Таймера1, вы обнуляете предделител.

???

да, я знаю про предделитель, согласен, возникает гуляющая нестабильность в максимум 8 тактов 1 раз в минуту
если принять среднее 4, то при тактовой 32768 это даст порядка 0.7сек в сутки
предусмотрена программная коррекция, заодно чтоб и частоту кварца не подгонять кондёрчиками

но к проблеме прерывания это никак не относится

касаемо метки, да, она "iend", одна буква потерялась почему то при копи-пасте, и почему то не могу отредактировать пост
Smen
Цитата(ddd-ekb @ Oct 28 2015, 20:35) *
есть какие то ссылки на аппноты по данной теме?
Дык, п. 6.4 даташита.
Подозреваю, что проблема именно в атомарности операции.
В более современных чипах (например 887) эта проблема решена аппаратно, а в старых приходилось изгаляться.
P.S.: Кстати, насколько помню, рекомендовалось таймер, при этом, вообще выключать (в примере этого нет).
ddd-ekb
Цитата(Smen @ Oct 29 2015, 09:58) *
Дык, п. 6.4 даташита.
Подозреваю, что проблема именно в атомарности операции.
В более современных чипах (например 887) эта проблема решена аппаратно, а в старых приходилось изгаляться.
P.S.: Кстати, насколько помню, рекомендовалось таймер, при этом, вообще выключать (в примере этого нет).

не понял, п 6.4 посвещён внешнему счёту вроде как
------------------
6.4 Timer1 Operation in
Asynchronous Counter Mode
If control bit T1SYNC (T1CON<2>) is set, the external
clock input is not synchronized. The timer continues to
increment asynchronous to the internal phase clocks.
The timer will continue to run during SLEEP and can
generate an interrupt on overflow, which will wake-up
the processor. However, special precautions in software
are needed to read/write the timer (Section 6.4.1).
In Asynchronous Counter mode, Timer1 cannot be
used as a time-base for capture or compare operations
-----------------------
тут же таймер используется с внутренней osc/4 и предделителем :8
T1CON инициализирован "00110101", согласно страницей даташита выше, бит 2 ( синхронизация внешней частоты ) игнорируется, если бит 1 = 0 (использование внутренней частоты )
-------------------
bit 7-6 Unimplemented: Read as ‘0’

bit 5-4 T1CKPS1:T1CKPS0: Timer1 Input Clock Prescale Select bits
11 = 1:8 Prescale value
10 = 1:4 Prescale value
01 = 1:2 Prescale value
00 = 1:1 Prescale value

bit 3 T1OSCEN: Timer1 Oscillator Enable Control bit
1 = Oscillator is enabled
0 = Oscillator is shut-off (the oscillator inverter is turned off to eliminate power drain)

bit 2 T1SYNC: Timer1 External Clock Input Synchronization Control bit
TMR1CS = 1:
1 = Do not synchronize external clock input
0 = Synchronize external clock input
TMR1CS = 0:
This bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.

bit 1 TMR1CS: Timer1 Clock Source Select bit
1 = External clock from pin RC0/T1OSO/T1CKI (on the rising edge)
0 = Internal clock (FOSC/4)

bit 0 TMR1ON: Timer1 On bit
1 = Enables Timer1
0 = Stops Timer1
----------------------
да даже и в этом случае, ну пусть сбои счёта происходили какие то, но не двойное же выполнение прерывания

зы: пока играюсь дальше с прошивкой, вроде вырисовывается зависимость, но опять же дурацкая какая то sm.gif
evc
Цитата(ddd-ekb @ Oct 29 2015, 09:03) *
...

По поводу предделителя и буквой "i" я тоже так прикинул, как вы потом и написали.
По поводу таймера и п.6.4, там действительно несколько строчек, которые меня тоже насторожили,
но имеется ввиду (в п. 6.4. и потом), что если во время записи произойдет переполнение таймера,
результат может быть непредсказуемым.
Если вы от нас ничего не скрываете (вы не обижайтесь, я немножко отклонюсь: приходят сюда, значит, [не]определенные товарищи и подбрасывают
всякие казусы. Мы начинаем здесь ломать копья, стрелы, организуем интеллектуальные [и не очень] битвы титанов, и наконец, где-то на десятой странице
обсуждения, когда чуть ли не мы уже подрались здесь всем форумом, приходит топик стартер и такой, "а вот и не угадали! у меня MCLR был не подключен, вот и болтался в
воздухе, вот и происходили случайные сбросы." и ставит три улыбочки, для подчеркивания эффекта.), значит нужно посмотреть другое: Судя по вашим продуманным (в общем) действиям,
с железом у вас все в порядке. Но не помешало бы, еще раз перепроверить. Потом. Это действительно старый процессор. Я смотрю, вы весь PIR1 обнуляете. Но это - только периферийные
прерывания. Есть еще и в INTCON-е. Не забывайте, что, например Таймер0 нельзя отключить, он всегда работает, даже и если настроить его брать такт с T0CKI.
Но! Самый главный подводный камень - по-страничная организация памяти. Если у вас осуществляется переход (типа goto, return, retlw и т.д.) из одной страницы на другую,
то это вам нужно позаботиться правильно переключить биты RP0 и RP1 в STATUS-е. Скорее всего это именно ваш случай. Таким образом вы можете попасть
аккурат в середину обработчика прерываний и он исполнится. Другая очень часто встречаемая ошибка - табличное чтение. Если при чтении из таблицы (вычисляемый офсетный переход),
вы проскочите и попадете в пустую область, программный счетчик просто перелопатит всю программную память и вернется на адрес 0. Опять со всеми вытекающими. Для этого перед табличным чтением
всегда нужно проверять аргумент функции - не превышает ли его значение количество объектов в таблице.
Также, имейте ввиду (хотя, это вряд ли ваш случай), при переполнении или при попытке уменьшать указатель стека ниже ноля, происходит сброс. Это может случится, например,
если по какой-то причине не сбросился флаг прерываний (IF).
Так что - посмотрите все очень внимательно и если есть новые соображения - спрашивайте.
Smen
Цитата(ddd-ekb @ Oct 29 2015, 10:03) *
п 6.4 посвещён внешнему счёту вроде как
В общем, да, но там есть такая фраза:
For writes, it is recommended that the user simply stop the timer and write the desired values. A write contention may occur by writing to the timer registers, while the register is incrementing. This may produce an unpredictable value in the timer register.
И тут скорее дело не в режиме, а в атомарности операции.
Насколько помню, делал следующим образом:
- Останавливал таймер
- Прибавлял (именно прибавлял) "корректирующее" значение, учитывающее и количество МЦ на данную процедуру.
- Включал таймер
Операция сложения использовалась что бы исключить влияния запаздывания выполнения обработчика прерывания (т.к. были и другие прерывания с большим приоритетом).
P.S.: А с кварцем-то всё в порядке? Может пока попробовать временно затактироваться от внешнего источника?
ddd-ekb
по порядку
1 ничего не скрываю, разработкой всякой мелочи на пиках и атмелах занимаюсь 20+ лет, до сих пор с таким выносом мозга на ровном месте не сталкивался
2 с железом явно всё в порядке, просто потому что его там нет как такового, MCLR просто повешен на питание перемычкой, сброс или сам произойдёт или вачдогом сбросится (он активирован )
3 таймер0 меня не особо занимает здесь, пусть хоть работает, хоть нет, прерывание запрещено от него, плюс я ставил в обработчик проверку сторонних битов прерываний, не обнаружил ни разу их, в стартовом посте писал об этом
4 страничная память знаю, да, для buf1 и buf2 назначены адреса 70Н и 71Н, чтоб они всегда адресовались однозначно, общая длина кода около 500 байт, так что проблем с GOTO и CALL не должно быть
5 табличные переходы есть, но прямо перед переходом значение принудительно урезается ANDLW 15 и сама таблица дополнена пустыми строками, так что за пределы выйти невозможно даже при вызове с ошибочным аргументом
6 про остановку-запись-пуск таймера не вижу необходимости в данном случае абсолютно никакой, я просто старший байт корректирую и всё, младший пусть считает сам по себе
7 менять кварц смысла тоже нет, т.к. при имитации прерывания программным путём ( постоянными вызовами CALL 4 ) всё работает безошибочно

а теперь наблюдение, выявил зависимость, с утра и до сейчас убежало на ровно 3 минуты
от перестановки команды CLRF PIR1 внутри обработчика это не зависит никак ( что и предполагалось и почему меня так удивили первые 2 ответа )
убегает если прерывание происходит в момент работы с регистрами банка1
в обработчике как я вижу на эту тему всё корректно, буферные регистры общей адресации для всех банков, статус сохраняется-восстанавливается, в обработчике статус обнуляется для корректного обращения к используемым там регистрам
но, тем не менее, оставлял на ночь с прошивкой где перед всеми BSF STATUS,RP0 натыкал BCF INTCON,7, а после CLRF STATUS обратно BSF INTCON,7 - до утра дошло без ошибок
утром убрал запреты прерываний перед обращением к регистрам на ненулевой странице - вот вылезло +3 минуты
т.е. найдено из-за чего обработчик вызывается иногда дважды
но не могу понять почему
хоть тресни



ViKo
Думаю, ошибка в прерывании. Не могу вникать-вспоминать во все нюансы, но у меня обработчик был сделан так.
Код
    org    4; Вектор прерывания
Intr    movwf    WorkSv; work save in any bank (all)
    swapf    Status, w; read status
    clrf    Status; bank 0, IRP=0
    bsf    Status, RP0; bank 1
    movwf    StatusSv; save in bank 1
;    bsf    Test; test!
    movfw    PCLatH; read high address
    movwf    PCLatSv; save in bank 1
    movlw    high(PageVect)
    movwf    PCLatH; PCLatH -> page 3
    goto    IntrPageVect
...
    bsf    Status, RP0; bank 1
    movfw    TempSv
    movwf    Temp
    movfw    FSRSv
    movwf    FSR
    movfw    PCLatSv
    movwf    PCLatH
;    bcf    Test; !
    swapf    StatusSv, w    
    movwf    Status
    swapf    WorkSv; in any bank (interrupted prog)
    swapf    WorkSv, w
    retfie


swapf - эта команда не просто так используется. Там что-то с флагами связано. Она их не портит, помнится. PIC16F876.
ddd-ekb
Цитата(ViKo @ Oct 29 2015, 17:05) *
swapf - эта команда не просто так используется. Там что-то с флагами связано. Она их не портит, помнится.

а ведь точно, MOVF портит Z
блин, ёмаё, этож надо так лохануться
хотя всёж не понимаю как это тут привязано, но косяк явный, да
а я как в середине 90х на первой программе к 16f84 этот вход-выход прерывания написал, так на автопилоте и использую
сейчас поправлю и поставлю проверяться sm.gif
ViKo
Цитата(ddd-ekb @ Oct 29 2015, 15:20) *
а я как в середине 90х на первой программе к 16f84 этот вход-выход прерывания написал, так на автопилоте и использую

В программе "Фобос-Грунт" не участвовали? rolleyes.gif
ddd-ekb
Цитата(ViKo @ Oct 29 2015, 17:27) *
В программе "Фобос-Грунт" не участвовали? rolleyes.gif

увы sad.gif
справедливости ради, сейчас тупо задал поиск Z по всему asm-файлу, нашёл только в 2 местах абсолютно некритичных ( 1 - гашение незначащего нуля в десятке часов перед выводом в порт, 2 - мигание разделителя часы-минуты )
так что вопрос отстаётся открытым, прерывание поправил по рекомендации даташита, поставил на проверку sm.gif
evc
ddd-ekb, слово "скрываете" я использовал в шутку. ViKo, однако прав, я давно на ассемблере не писал, вот и забылось.
Как я вам и писал - именно адресация страниц портится у вас, в обработчике прерываний. Уважаемый ViKo, навел на правильную мысль.
Попытайтесь поставить вот такую преамбулу обработчика прерываний и соответственно на выходе тоже обратный код:

Код
        ORG     0x004            ; interrupt vector location
        movwf   w_temp           ; save off current W register contents
        movf    STATUS,w         ; move status register into W register
        movwf    status_temp      ; save off contents of STATUS register
        movf    PCLATH,w
        movwf    PCLATH_temp
        clrf    PCLATH

... здесь ваш обработчик прерываний...

        movf    status_temp,w    ; retrieve copy of STATUS register
        movwf    STATUS           ; restore pre-isr STATUS register contents
        movf    PCLATH_temp,w
        movwf    PCLATH
        swapf   w_temp,f
        swapf   w_temp,w         ; restore pre-isr W register contents
        retfie                   ; return from interrupt
ddd-ekb
Цитата(evc @ Oct 29 2015, 17:41) *
именно адресация страниц портится у вас, в обработчике прерываний.

да и чёрт с ней, с этой адресацией, пусть хоть вся перепортится
в данной программе на каждом проходе принудительно устанавливается состояние всей периферии
затем просто берутся значения из регистров минут и часов, перекодируются в сегментный вид и выплёвываются в порты
так что все сбои могут привести только к сбою индикации на от силы 50-100 миллисекунд
вобщем пока пусть проверяется, первые полчаса прошли нормально
evc
Цитата(ddd-ekb @ Oct 29 2015, 16:03) *
да и чёрт с ней, с этой адресацией, пусть хоть вся перепортится

из-за этого получаете несанкционированные входы в обработчик прерываний ... но как скажете.
ddd-ekb
Цитата(evc @ Oct 29 2015, 18:11) *
из-за этого получаете несанкционированные входы в обработчик прерываний ... но как скажете.

на сейчас окончательно утряс причину возникновения повторного прерывания сразу при выходе из обработчика
несанкционированных входов нет sm.gif
в основной программе есть периодическая инициализация регистра PIE1
MOVLW 1
MOVWF PIE1
на всякий случай, вместе с остальными TRISами и ADCONами
вдруг чего сбойнёт от помех и некому будет питание передёрнуть, а вачдог не сработает, т.к. программа то бегает по кругу, просто периферия переинициализировалась неверно из-за таджика-сварщика sm.gif
раз уж методом тыка установлено влияние страниц, то логично предположить что обработчик и правда не восстанавливает страницу, тогда получается что при попадании момента прерывания между выбором страницы 1 и записью 1 в PIE, эта 1 отправляется в зеркальный относительно PIE регистр PIR и устанавливает этот самый флаг, который сразу же опять и вызывает прерывание
абсолютно понятно и логично, ура sm.gif
теперь осталось понять в каком месте обработчика портится страница, т.е. бит 5 регистра статуса
Код
    movwf    buf1
    movf    STATUS,w
    movwf    buf2
    clrf    STATUS
.......
    movf    buf2,w
    movwf    STATUS
    movf    buf1,w
    retfie

единственная портящая статус команда тут MOVF, и портит она только бит 2
да и в принципе, если эта команда портит биты выбора страниц, то как ей пользоваться на ненулевых страницах?
на всякий случай повторюсь, buf1 и 2 имеют адреса 70Н и 71Н для однозначной адресации независимо от текущей страницы
так что половина непонимания остаётся пока

три тысячи чертей, они опять упрыгали на 2 минуты вперёд, с новым обработчиком прямо скопипастеным с даташита, со всеми этими SWAPами

вобщем точно выходит что обработчик не восстанавливает обратно 1 страницу, а оставляет 0 на которой сам работал

угу, абсолютно точно так, набил 50 нопов между выбором 1 страницы и записью в PIE1 (чтоб расширить окно ошибки), так прямо на глазах по 2 минуты плюсует
evc
Цитата(ddd-ekb @ Oct 29 2015, 19:39) *
три тысячи чертей

Нет, только один - PCLATH.
Я наверное, старею, поэтому все таки вам отвечу: в STATUSе хранятся биты для определения страницы оперативной памяти.
В PCLATHе хранятся биты для определения страницы программной памяти.
Еще в первом посте на этой странице я вам привел код для обработчика который сохраняет и то и другое, чего в вашем коде нет.
Но, к сожалению, выходит правда - очевидные вещи, самые малозаметные - вы вцепились в этот STATUS и в упор не замечаете PCLATH.
ddd-ekb
Цитата(evc @ Oct 29 2015, 21:28) *
Нет, только один - PCLATH.
Я наверное, старею, поэтому все таки вам отвечу: в STATUSе хранятся биты для определения страницы оперативной памяти.
В PCLATHе хранятся биты для определения страницы программной памяти.
Еще в первом посте на этой странице я вам привел код для обработчика который сохраняет и то и другое, чего в вашем коде нет.
Но, к сожалению, выходит правда - очевидные вещи, самые малозаметные - вы вцепились в этот STATUS и в упор не замечаете PCLATH.

ура, всё, раскурил до конца biggrin.gif
PCLATH не при делах, как и программная память
проблема как и было в итоге определено в неверном сохранении обработчиком номера страницы регистров
причина в том, что у 73\74 нет общеадресуемой памяти, в отличии от 76\77, а даташит общий
вот и не отразил сходу этот момент

усё, спасибо за попинывание и всякие мысли, тему закрыть, автора в бан santa2.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.