Главная Мой профиль Регистрация Выход Вход
Приветствую Вас Гость | RSS
Вторник
04.02.2025
19:55
Articles
Меню сайта
Категории каталога
Счетчик TMR0 [2]
Прерывания [2]
ШИМ на pic16f877 [1]
Configuration Bits in Mid-Range PIC Microcontrollers [1]
Индикация [1]
Мини-чат
Главная » Статьи » Программирование MK PIC » Счетчик TMR0

Управление счетчиком Tmr0 для pic

оригинал этой статьи здесь

В начале комментарии.

Прежде всего нужно сказать, что формирование интервала должно происходить ИСКЛЮЧИТЕЛЬНО в ДВОИЧНОЙ форме. Создание образцового интервала кратного целому числу секунд(миллисекунд, микросекунд), а затем повтор этих интервалов - СОВЕРШЕННО ДУРНОЙ ТОН программирования. При этом накапливаются погрешности формирования этого интервала.
Есть простой и надежный алгоритм создания временной выдержки любой длительности. Для этого вычитают из заданного интервала часть кратную степени числа два и находят остаток. Такм образом вначале производят предустановку системы числом равным ДОПОЛНЕНИЮ этого остатка до переполнения системы счета и первый интервал счета будет равен остатку, а затем считают целое количество переполнений ПОЛНОГО ПЕРЕСЧЕТА двоичного счетчика.
Для прояснения ситуации необходимо рассмотреть схему работы этого алгоритма в МК.



Итак, входная частота есть в данном случае частота осциллятора деленная на 4 (пусть для простоты будет 4 МГц/4 -> 1 мкс - период входных тиков. Черт, я не оригинален... )
Нужно учесть, что совершенно не обязательно считать с разрешением в 1 мкс, поскольку стабильность и точность самого осциллятора (кварца) не такая уж высокая. Поэтому для уменьшения разрядности пересчета полезно включить на входе системы прескалер(предделитель). Он в среднем семействе пиков может быть установлен с делением от 1 до 256 с шагом в 1 разряд двоичного счетчика (2,4,8,16,32,64,128,256). Для этого в младших трех разрядах регистра OPTION_REG устанавливают код этого коэффициента деления. Можно в этом же регистре отключить прескалер вообще и тогда 1 мкс напрямую будет инкрементировать таймер TMR0. Когда TMR0 переполняется, то сигнал переполнения устанавливает в 1 третий разряд регистра INTCON и этот разряд в свою очередь разрешает (при условии наличия разрешений соответствующих прерываний) непосредственно само ТРЕБОВАНИЕ прерывания и вызывает переход по вектору 4.
В обработчике прерываний размещенным начиная с этого четвертого адреса производится уменьшение на 1 (декремент) программного счетчика, который предварительно установлен перед пуском. И когда содержимое этого счетчика будет равно нулю, интервал пересчета закончен. Этот счетчик может быть сделан ЛЮБОЙ кратной 8 разрядности. При этом декремент всего многобайтного счетчика ведется в несколько команд с учетом переноса.
Общий алгоритм выглядит так.
1. Инициализируем TMR0 установкой его конфигурации (прескалер, источник тиков) в регистре OPTION_REG. Не забываем, что этот регистр в первом банке!
2. Записываем в счетчик переполнений (тот что в обработчике декрементируется) целое количество переполнений TMR0 плюс одно (это то, что неполное в начале).
3. Записываем в TMR0 ДОПОЛНЕНИЕ до числа 256 этого самого остатка, который мы нашли в начале при расчете пересчетных установок.
4. Устанавливаем пин порта в который выводится интервал в 1.
5. Сбрасываем флаг переполнения TMR0 в регистре INTCON
6. Разрешаем глобальные прерывания (Прерывания от TMR0 можно разрешить в самом начале при инициализации).

7. Обработчик прерываний содержит сохранение контекста (аккумулятор и регистр статуса)
8. Далее следует семафор прерываний (это если у нас не только эта задача в обработчике)
9. Декремент счетчика (в примере счетчик однорегистровый, но можно и увеличить его разрядность)
10. Проверка этого счетчика на ноль и сброс пина порта на который выводится интервал.

Ниже приводится пример реализации этого кода.
org 0x00

nop
goto main
;-----------------------------------------------
org 0x04

movwf w_temp
movf STATUS,w
movwf status_temp

btfsc INTCON,T0IF
goto int_Timer
.........................................
.........................................

int_Timer decfsz Timer,f
goto $+4
bcf PORTB,1
bcf INTCON,T0IE
bcf INTCON,T0IF

movf status_temp,w
movwf STATUS
swapf w_temp,f
swapf w_temp,w
retfie
.........................................
.........................................

main


.........................................
.........................................

bsf STATUS,RP0
movlw b'00000nnn'
movwf OPTION_REG
bcf STATUS,RP0
.........................................

movlw preset_Timer
movwf Timer
.........................................

movlw preset_TMR0
movwf TMR0
bcf INTCON,T0IF
bsf PORTB,1
bSf INTCON,GIE

; с этого места пошел отсчет интервала

Вот смотрите пример расчета. Положим нужно 10 секунд. Кварц 4 МГц - тики на прескалер 1 мкс. Так как время большое, то берем прескалер скажем 1:256. Значит на таймер TMR0 будут приходит тики уже 256 мкс. Делим 10 000 000 мкс (10 сек) на 256 мкс входных тиков и еще на 256 пересчетов этих тиков самим TMR0. 10 000 000/256*256=152,58... значит необходимо пересчитать 152 полных переполнений таймера и еще одно с предзагрузкой. Считаем время на полные переполнения: 152*256 мкс*256переполнений=9 961 472мкс. Разница с нужными 10 сек составляет 38 528 мкс. Делим их на пересчет прескалера: 38528/256=150,5 - половина останется погрешностью (или нужно пересчитать на прескалер 128, но тогда счетчик переполнений таймера будет двухбайтным - нужно 9 разрядов, а не 8). Положим возьмем 150 (пусть будет недобор 128 мкс).
значит необходимо предустановить таймер перед первым пересчетом на дополнение этого числа 150 до 256, получается 256-150=106.
Итак результат:
 В прескалер пишем деление 256 (последние разряды OPTION_REG - 111)
В счетчик переполнений заносим 153 (это 152 полных и одно неполное)
В таймер предустанавливаем 106.
Таким образом первый цикл до переполнения таймер насчитает 150*256=38400 мкс, а потом будет 152 полных цикла пересчета таймера 152*256*256=9961472 мкс, итого вместе 9999872 мкс или 9,999872 сек - погрешность меньше 0,002% (но не точность, она зависит от стабильности кварц. генератора МК и еще есть погрешность латентности прерываний...

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

вот статья Владимир Д. с радиокота

1.Применение прерываний от переполнения таймера TMR0 (RTCC)


Примем тактовую частоту - Fтакт. = 4,096 МГц (стандартный кварц). Тогда время цикла составит t c = 1 / Fтакт. * 4 = 0,97656 мкс


INI_TMR ; инициализация режима прерываний от RTCC
bsf STATUS,RP0 ; выбираем банк 1
movlw b'00000100'
movwf OPTION ; предделитель для RTCC 1 : 32
bcf STATUS,RP0 ; банк 0
movlw b'10100000'
movwf INTCON ; разрешено прерывание от RTCC
movlw .96 ; загружаем в RTCC предварительное число 96
movwf TMR0


Получим время прерываний:
t i = t c * 32 * (256 - 96 = 160)
t i = 0,97656 * 32 * 160 = 5 000 мкс = 5 мс

Теперь, если в Вашу любую программу ввести бесконечный цикл (так называемый цикл ожи- дания прерывания), и окончание программы переводить на этот цикл, получим временную привязку к 5 мс.И после прерывания программа вернётся по адресу, указанном вектором прерываний (чаще это 04h).Для чего это можно использовать - смотри дальше.

Итак:

;
org 0
START ; начало выполнения программы после
; включения питания
org 04h ; а это адрес вектора прерывания, по которому
main ; будет выполняться основная программа
;
START ; здесь обычно происходит обязательная ини-
INI_TMR ; циализация портов, режимов, регистров и т.п.
INI_PORTS
loop
goto loop ; а это и есть бесконечный цикл

;--------------------------------------------------

main
; далее идёт тело основной программы,
; в которой обязательно надо создать программу обслуживания прерываний от RTCC,
; вызываемой командой CALL:

ServTMR
btfsc INTCON,RTIF ; проверяем флаг срабатывания прерываний от RTCC и
call SET_TMR ; если "да",то снова инициализируем TMR0
return ; если "нет" - возврат в место вызова ServTMR в
; основной программе main
;
SET_TMR movlw .96
movwf TMR0 ; снова загружаем число 96
bcf INTCON,RTIF ; сбрасываем флаг срабатывания
retfie ; возврат с разрешением прерываний в ServTMR, а
; затем в основную программу main

Пример использования прерывания от RTCC для получения секундного импульса на одном из выходов , скажем, порта В - RB0 : Используем регистр Rsec, который должен быть ранее объявлен в в адресном поле рабочих регистров.

FORM_1S ; в каждом цикле, а он по прерыванию RTCC длится
incf Rsec,w ; 5 Мс, увеличиваем регистр Rsec на 1 до числа 200
xorlw .200 ; (5 мс * 200 = 1 сек)
btfsc STATUS,z
goto OUT_PORT ; при Rsec = 200 флаг z = '1' и переход на управление
; выводом RB0 порта В
return ; возврат в основную программу main
;
OUT_PORT btfss PORTB,0 ; проверяем состояние вывода RB0
goto OUT_ON ; если RB0 ='0', то устанавливаем в '1'
bcf PORTB,0 ; в противном случае - устанавливаем в '0'
goto main ; возврат в основную программу
;
OUT_ON bsf PORTB,0 ; устанавливаем RB0 = '1'
goto main


Таким образом на выходе RB0 порта В каждую секунду уровень сигнала будет изменяться то '0' то '1'.

В регистрах контроллера информация находится обычно в двоичном виде, ( в бинарном коде). Но часто необходимо получить информацию в двоично - десятичном виде (BCD - код), скажем, для управления поразрядно семисегментным индикатором.

Рассмотрим примеры преобразований двоичного кода b2 в двоично - десятичный BCD и наоборот.

В 8 - bit регистре можно записать в двоичном коде число от 0 до 255 ( от b'00000000' до b'11111111' ). Преобразуем двоичное число в три разряда двоично - десятичного кода - "сотни", "десятки" и "единицы". Для этого будем использовать следующие регистры, которые должны быть заранее объявлены в адресном поле рабочих регистров :

Rbin - регистр хранения числа в двоичном коде b2
Rhan - регистр "сотни" кода BCD
Rdec - регистр "десятки" кода BCD
Rsim - регистр "единицы" кода BCD

Преобразования проводим используя операции вычитания чисел 100, а затем 10 с подсчётом количества положительных вычитаний.

CON_100 movlw .100 ; вычитаем 100 из Rbin c проверкой, что
subwf Rbin,w ; результат не отрицательный. Флаг 'c' = 1 при
btfss STATUS,c ; результате > или = 0, и 'c' = 0 при < 0
goto CON_10
incf Rhan,f ; подсчёт количества "сотен"
movwf Rbin ; результат вычитания сначала храним в регистре
goto CON_100 ;аккумуляторе и только потом возвращаем в Rbin
; чтобы не потерять остаток при отрицательном
; результате вычитания.
CON_10 movlw .10 ; аналогично определяем "десятки"
subwf Rbin,w
btfss STATUS,c
goto end_con
incf Rdec,f
movwf Rbin
goto CON_10;
end_con
movf Rbin,w
movwf Rsim ; после вычитаний заносим остаток в "единицы"
;продолжение выполнения программы


Обратное преобразование BCD - кода в b2. Используем те же регистры Rhan, Rdec, Rsim где находится число в BCD - коде, регистры RbinH - старший разряд и RbinL - младший разряд для чисел ( > 255) в коде b2 и вспомогательные регистры RM1 - "множимое" , RM2- "множитель".Для преобразования BCD в b2 нужно умножить "сотни" на 100, "десятки" на 10 и сложить всё вместе с "единицами" и с учётом переноса в старший разряд при необ- ходимости.Для умножения используем операцию сложения.

B2X_100 movlw .99 ; преобразование "сотен"
movwf RM2 ; множитель = кол - во сложений (100) минус один
movf Rhan,w
movwf RM1 ; множимое = "сотни"
loopX100 addwf RM1,w
btfsc STASTUS,c ; проверяем перенос в старший разряд
incf RbinH,f ; если есть перенос
decfsz RM2,f ; контролируем количество сложений
goto loopX100
movwf RbinL ; результат сложения заносим в регистр мл. разряда
;
B2X_10 movlw .9 ; преобразование "десятков"
movwf RM2 ; множитель = кол - во сложений (10) минус один
movf Rdec,w
movwf RM1 ; множимое = "десятки"
loopX10 addwf RM1,w ; здесь перенос можно не проверять, т.к. результат
decfsz RM2,f ; всегда < 255
goto loopX10
addwf RbinL,f ; добавляем результат преобразования "десятков"
btfsc STATUS,c ; учитывая возможный перенос в разрядах
incf
RbinH,f
movf Rsim,w
addwf Rbin,f ; добавляем "единицы" с учётом возможного переноса
btfsc STATUS,c
incf RbinH,f


Конец преобразованиям и дальнейшее выполнение программы. В регистрах RbinL и RbinH получили 16 - bit число в коде b2.

Для выполнения арифметической операции деления по аналогии с умножением, рассмот- ренном выше, применяется операция вычитания. Допустим нам нужно произвести деление числа, находящегося в регистрах RHsum (старшие разряды) и RLsum (младшие разряды) - на делитель ( примем делитель не > 255) находящийся в регистре Rdel.

Результат будем заносить в регистры RHrez и RLrez (старшие и младшие разряды соот- ветственно) :


OP_DEL
movf Rdel,w
subwf Rlsum,w
btfss STATUS,c ; проверяем не отрицательный ли результат?
goto DEF_carry ; если "да", то проводим заём из ст. разряда
incf RLrez,f ; подсчитываем кол-во вычитаний с учётом
btfsc STATUS,c ; возможного переноса в старший разряд
incf RHrez,f
movwf RLsum ; восстанавливаем остаток, что бы не потерять
goto OP_DEL ; при отрицательном результате вычитания
;
DEF_carry
movlw 0h
xorwf RHsum,w ; всё ли заняли из старшего разряда в младший?
btfsc STATUS,z ; если "да", т.е. RHdel = 0 и в OP_DEL отри-
goto OUT_ DEL ; цат. результат - конец делению и выход
decf RHsum,f ; если "нет" - заём из старшего разряда и про-
incf RLrez,f ; должаем дальше
btfsc STATUS,c ; проверка необходимости переноса в ст.разряд
incf RHrez,f
goto OP_DEL


Категория: Счетчик TMR0 | Добавил: Олег (03.02.2013)
Просмотров: 4579 | Рейтинг: 0.0/0 |
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Поиск
Друзья сайта
Статистика

Онлайн всего: 1
Шпионов 1
Пользователей: 0

Copyright MyCorp © 2025