Главная » Статьи » Proton PICBasic

Модуль CCP. Режим захвата

Не буду описывать все устройство, так как оно является  частью коммерческой разработки. В общем, устройство разработано.

Итак, режим захвата, модуль CCP. Мы будем рассматривать модуль CCP1, так как в устройствах с двумя модулями они работают одинаково, за исключением небольшого нюанса. Об этом вы можете прочитать в приложении от Microchip на русском языке, которое находится в прикрепленном архиве с проектом.

Во-первых, модуль CCP представляет собой 8-разрядный регистр управления CCP1CON, 16-разрядный рабочий регистр, состоящий из двух 8-разрядных регистров CCPR1L и CCPR1H, и непосредственно вывод CCP1, который может быть как входом, так и выходом, в зависимости от режима работы модуля. В режиме захвата, этот вывод настраивается на вход. Также модуль, в зависимости от режима работы может взаимодействовать либо с таймером TMR1, либо с таймером TMR2. В режиме захвата модуль взаимодействует с таймером TMR1. Смысл работы режима захвата в том, чтобы захватить значение TMR1 в момент прихода импульса. Самый простой вариант измерения периода заключается в подсчете количества импульсов известной длительности между, умещающимися между двумя фронтами одного измеряемого периода. Подсчет заключается в фиксировании текущего значения TMR1 в момент прихода импульса на вход CCP1.

Попытаюсь изобразить на рисунке, как работает этот модуль в режиме захвата.


Итак, TMR1 настраивается на тактирование от внутреннего источника. Если предделитель равен 1:1, то инкремент таймера при частоте кварцевого резонатора (или другого) 4 МГц, будет происходить каждую микросекунду. Соответственно, то, что насчитает таймер – будет количеством микросекунд. Нетрудно перевести в миллисекунды и секунды. На верхней диаграмме показана именно частота тактирования TMR1. Ниже изображен вход CCP, на который поступает измеряемый сигнал. Если мы настроим захват по каждому фронту на входе CCP, то каждый раз, как приходит импульс, содержимое таймера TMR1 будет переписываться в CCPR1 – иными словами, регистр CCPR1 будет захватывать содержимое таймера. Это показано на рисунке. Следует заметить, что на рисунке частота на входе CCP показана условно. При такой частоте программа слишком часто будет уходить в прерывания и 
 будет мешать выполняться программе. На рисунке изображен именно этот случай. Что бы избежать этого, можно настроить захват по каждому 4 или каждому 16 фронту, а предделитель таймера настроить на 1:8, а в программе делать  соответствующие корректировки. Мы рассмотрим это в программе. Там сделано именно так. В общем, все должно работать как надо, вот только я никак не пойму, почему в регистре CCPR1H:L оказывается число, не соответствующее измеряемому периоду. По инструкции - по приходу положительного фронта на CCP1 - в регистре CCPR1H:L должно находиться насчитанное с последнего фронта значение TMR1. В реальности - там оказывается число, меньше на 3 - 4 единицы. По идее, это - аппаратный механизм. В общем, мы подумали и я решил, что можно применить другой алгоритм. При захвате устанавливается флаг CCP1IF, по которому, если это разрешено, мы попадаем в прерывание. В прерывании мы просто считываем значение TMR1 и обнуляем таймер. Таким образом, отсчет начинается сначала, а мы имеем значение периода. Так как мы знаем длительность такта, то и период нам известен! 

Вы можете протестировать данную стратегию на небольшом симуляторе, который я сделал специально для этой статьи (для этого у вас должен быть установлен Flash player). В симуляторе вам придется установить биты, разрешающие прерывание при возникновении захвата, а также прерывание при переполнении таймера TMR1. В блоке 'Обработчик прерывания' вам придется самостоятельно сбрасывать флаги прерываний. Нажмите на знак вопроса, чтобы прочесть справку.

Здесь рассмотрен случай, когда частота осциллятора намного больше, чем частота входного сигнала. И еще один момент. Технология Flash не позволяет обрабатывать слишком быстрые события, кроме того, чтобы увидеть изменения всех флагов и регистров, нужно замедлить скорость выполнения, что я и сделал. В данном симуляторе подразумевается, что используется кварц на 4 МГц. Следовательно 1 машинный цикл равен 1 мкС. Но в симуляторе за 1 мкС взята единица времени, равная 50 мС. Поэтому при отсутствии входной частоты и включенном таймере, ждать его переполнения нужно 65535*50/1000 = 3276,8 мС ~ 54 мин.  
 Вы это проследите, если запустите этот ролик на своем компьютере. В веб браузере таймеры ролика работают некорректно, поэтому при ожидаемом в TMR1H:L значении вы увидите другое. Поэтому, если есть желание протестировать работу ролика, запустите его на компьютере, скачав архив с программой и схемой.  Также вы для изучения можете настроить предделитель таймера TMR1. В общем, изучайте.




А вот и сама программа. Внимательно читайте комментарии.

'-------------------------- Описание проекта ---------------------------------

'Проект :  Применение модуля захвата для вычисления периода импульса

'Автор :  Admin

'Версия : 1.0.0 от 08.12.2011   

'-------------------------- Опции компилятора --------------------------------  

Declare  SHOW_SYSTEM_VARIABLES = OFF; ' При симуляции в Proteus не показывать внутренние переменные

Declare  Reminders = OFF    ' Выключить напоминания компилятора

Declare  Warnings = On   ' Выключить предупреждения компилятора

;-------------------------- Общие настройки------------------------------------

Device = 16F877A   ' Используемый микроконтроллер

Declare Xtal = 20   ' Частота осциллятора  20 МГц

                    ' Чем выше частота, тем выше точность измерения  

'-------------------------- Конфигурация программирования ----------------  

Config  CP_ALL, DEBUG_OFF, WRT_OFF, CPD_ON, LVP_OFF, BODEN_OFF, PWRTE_ON, WDT_OFF, HS_OSC  

'-------------------------- Настройки портов -----------------------------

Declare  All_Digital = On   ' Установить все порты цифровыми входами/выходами

Dim PERIOD As Word          ' Переменная для захвата значения периода

Dim FREQUENCY As Dword      ' Переменная для расчета частоты

Dim DigitDisp As Byte       ' Переменная для вывода текущей цифры в порт

Dim i As Byte               ' Переменная-счетчик

Dim j As Byte               ' Переменная-счетчик

Symbol Number = 2 * 20000000/' Постоянная величина - частота осциллятора деленная на 4

                                ' и умноженная на 2 (так как предделитель TMR1 = 1:8,

                                ' а захват производится по каждому 16 фронту)

Symbol PortOut = PORTC          ' Порт для сегментов  

'-------------------------- Регистры специального назначения---------------

'-------------------------- INTCON ---------------------------------------

Symbol CCP1IE = PIE1.2 ' Бит разрешения прерывания от модуля CCP                         

Symbol PEIE = INTCON.6 ' Бит разрешения прерывания от периферийных устройств

Symbol GIE = INTCON.' Бит глобального разрешения прерываний

Symbol TIMER = TMR1L.Word ' 16-разрядная переменная TMR1

Symbol CAPTURE = PIR1.2    ' Флаг прерывания от модуля CCP

'-------------------------- Начало -------------------------------------------

On_Interrupt GoTo  Int_Label ' При возникновении прерывания перейти на Int_label

    PORTA = %00000000   ' Обнулим PORTA

    PORTB = %00000000   '         PORTB

    PORTC = %00000000   '         PORTC

    PORTD = %11111111   ' Выключим катоды индикаторов

    PORTE = %000        '         PORTE

    TRISA = %00000000   ' Порт A - на выход

    TRISB = %00000000   ' Порт B - на выход

    TRISC = %00000100   ' Порт C - на выход, за исключением вывода CCP1

    TRISD = %00000000   ' Порт D - управление анодами

    TRISE = %000        ' Порт E - на выход

    OPTION_REG = %00100111 ' Этот регистр нам не нужен

    INTCON = 0             ' Запретим все прерывания

    CCP1CON = %00000111    ' Модуль CCP - в режиме захвата по каждому 16-фронту       

    T1CON = %00110001      ' Тактирование TMR1 от внутреннего источника предделитель 1:8

    TIMER = 0

    FREQUENCY = 0               

    PERIOD = 0

    CCP1IE = 1

    PEIE = 1

    GIE = 1    ' Разрешение глобального прерывания

    GoTo MainProgram                       

'-------------------------- Обратный отсчет--------------------------

Int_Label:

'-------------Захват периода-----------------------------

     If CAPTURE = 1 Then   ' Проверка флага прерывания                            

         PERIOD = TIMER    ' Захват в переменную значения, которое насчитал

                           ' TMR1 с последнего прерывания

         TIMER = 0         ' Обнуление TMR1, чтобы он опять считал с нуля

         CAPTURE = 0       ' Сброс флага прерывания

     EndIf      

    Context Restore        ' Возврат из обработчика прерывания

                           ' с восстановлением регистров W и Status    

'-------------------------- Главная программа -------------------------

MainProgram:

'----------Расчет частоты-----------------------------

    If PERIOD > 0 Then    ' Как мы помним из школы, на ноль делить нельзя

        FREQUENCY = Number/PERIOD  ' Получаем значение частоты - обратной функции от периода

    EndIf

'-------------Отображение частоты---------------------

    For i = 1 To 10     ' Для уменьшения "прыгания" значения частоты запустим цикл,

                        ' за время которого значение периода может изменяться

                        ' Но частота у нас уже посчитана!

        PORTD = %11111110   ' Для индикаторов с ОК - включим нулевой разряд индикатора

        For j = 0 To 7      ' И последовательно включая следующий разряд,

            DigitDisp = Dig FREQUENCY, j ' присваиваем переменной текущую позицию числа и

            GoSub Display                ' выводим ее в текущий разряд индикатора

            PORTD = (PORTD << 1) + 1     ' Сдвигаем ноль, которым зажигается разряд влево,

                                         ' зажигая следующий разряд

        Next

    Next i

GoTo  MainProgram

'-----------------------Вывод значений на дисплей-----      

Display: 

    PortOut = LookUpL DigitDisp,[$EB,$21,$BA,$B3,$71,$D3,$DB,$A1,$FB,$F3]

    ' Значения сегментов вычислялись с помощью

    ' этого инструмента, так как сегменты идут непоследовательно

    DelayMS 2    ' Задержка для видимости отображения        

    PortOut = ' Гасим сегменты для устранения послесвечения

    Return       ' Возврат

End


Скачать архив с материалами статьи можно здесь




Категория: Proton PICBasic | Добавил: ADMIN (12.01.2012)
Просмотров: 11990 | Комментарии: 8 | Теги: период, pic16f877, симулятор, таймер, предделитель | Рейтинг: 5.0/6
Всего комментариев: 8
1 terrarus   (14.01.2012 10:30)
Интересно а какая максимальная частота счета получается (в железе) и какую погрешность полчим например на частоте 1 мГц.

2 ADMIN   (15.01.2012 07:20)
Ну при 4 МГц разрешение 1 мкС. Но следует учитывать время на переход к обработчику и саму обработку, за которое может прийти еще импульс при высокой частоте. Вообще, если использовать кварц на 20 МГц, то можно получить хорошие результаты. Но, если подавать на вход слишком высокую частоту, то программа практически не будет выходить из обработчика прерываний. Поэтому нужно настройть предделитель CCPR1 на захват по каждому 16 фронту и корректировать результат.
При 1 МГц разрешение будет 2,5 мкС.
Максимально возможную частоту не имел возможности померять. Но думаю 150 кГц при 4 МГц генераторе - свободно. Мне не нужны были такие частоты.

3 vik1704   (17.01.2012 00:43)
Максим подскажи пожалуйста у меня компелятор PBP матерится при виде цифры 20000000 это как-то можно исправить?

4 ADMIN   (17.01.2012 01:28)
Это можно написать как 20*1000000

5 vik1704   (17.01.2012 23:11)
Максим я уже достал наверно но мне очень хочется разобраться в этой теме. Проблема в том, что РВР ни в какую не воспринимает строчки Dim FREQUENCY As Dword и Symbol TIMER = TMR1L.Word. Что касаемо 1 строки то здесь мне не понятно только описание DWord. А вот со второй строкой совсем беда. Здесь я так понял Word означает, что обработка должна вестись сразу двух байт таймера и младшего и старшего. А вот как это прописать в РВР?

6 ADMIN   (18.01.2012 07:42)
PBP не поддерживает Dword. Dword - это 4-байтная знаковая переменная. Это протон. Я же говорю, протон лучше PBP

7 ADMIN   (04.06.2014 18:36)
Есть ли у кого-нибудь вопросы по статье?

8 vanish   (29.06.2014 13:04)
Я изготовил девайс на базе Вашей программы, немного упростив ее, мне нужно всего 3 лед-индикатора, так как исследуемая частота не выше 999 гц. Частотомер показывает только нули.Так вот хотел спросить, какова чувствительность данного частотомера? 5вольт или я ошибаюсь? И еще как лучше построить входной каскад, на транзисторе, операционнике, оптроне?Спасибо.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]