Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Микроконтроллеры PIC фирмы Microchip для начинающих выпуск №29


РАССЫЛКА Микроконтроллеры PIC фирмы Microchip для начинающих
Ведущий: Алексей (pont_a@mail.ru)


Доброго времени суток, дамы и господа!!

Сегодня продолжаем рассматривать модуль захвата/сравнения CCP микроконтроллера PIC18F452! В этом выпуске мы будем писать программу, которая будет работать с CCP1 в режиме захвата!

Для начала, опишем алгоритм работы программы! Период будет представлен в тиках длительностью по 200 наносекунд (Fosc/4). Количество тиков в периоде располагается в трех байтах результата. Таким образом, мы можем подсчитать максимальный период сигнала, равный  200нсек*256*256*256=3,3554432 секунды. Результат усредняется по 32-м последним измеренным значениям. Реализовано, так называемое, скользящее среднее, когда результат усреднения обновляется при подсчете каждого нового значения периода. Минимальный период сигнала ограничен только временем выполнения обработчика прерывания.
Программу можно использовать, как базовую для собственных разработок. Можно, например, добавить отправку вычисленных данных по UART’у на ПК или подключить любой жидкокристаллический индикатор (ЖКИ), на котором будет отображаться частота или период.

И теперь, сама программа:
;===================================================================================
; подключаем стандартный заголовочный файл
#include <P18F452.INC>

; директивой LIST задаем тип микроконтроллера, формат HEX-файла
; и формат констант по-умолчанию - десятичный.
LIST    P=18F452, f=INHX32, R=DEC

; задаем биты конфигурации (как задавать, смотрите шестой выпуск)
CONFIG     OSC=HS      ; тактирование от кварца частотой 20МГц
CONFIG     OSCS=OFF    ; запретить переключать частоту тактирования микроконтроллера
CONFIG     PWRT=ON     ; включать задержку на 72мс после включения питания
CONFIG     BOR=ON, BORV=45   ; схему сброса по провалу питания включить. Порог=4,5В
CONFIG     WDT=ON, WDTPS=128 ; сторожевой таймер включить, период 2,3 сек
CONFIG     STVR=ON     ; по переполнению стека давать сброс микроконтроллера

; объявляем константы
ab equ 0    ; признак, что переменная находиться в access bank        
bb equ 1    ; признак, что адрес необходимо считать с использованием регистра BSR
rw equ 0    ; результат размещается в регистр-аккумулятор WREG
rf equ 1    ; результат размещается в регистр FREG

; адреса
ADDRESS_ACC_ARRAY equ  0x80  ;адрес начала накопительного массива
MASK_LENGHT_ARRAY equ  0x1F  ;маска ограничения длины буфера усреднения(32 значения)

; биты регистра статуса (status_reg)    
first_capture_b         equ   0        ; бит первого захвата
first_value_b           equ   1        ; бит первого измеренного значения
new_value_b             equ   2        ; признак нового значения в массиве усреднения   
; объявляем переменные
CBLOCK     0x000                   ; блок начинается с нулевого адреса банка 0
acc_reg0,acc_reg1,acc_reg2,acc_reg3 ; регистр-аккумулятор, для усреднения
rez_reg0,rez_reg1,rez_reg2        ; регистр - результата
cur_measure0,cur_measure1         ; текущее значение захвата
old_measure0,old_measure1         ; значение предыдущего захвата
CCPR1U                            ; третий байт данных CCP1( делаем программно)
status_reg                        ; регистр статуса
count                             ; счетчик циклов
array_ptr                         ; указатель на буфер усреднения
ENDC
; макросы
#define Disable_Int          BCF INTCON, GIE, ab
#define Enable_Int           BSF INTCON, GIE, ab
;-----------------------------------------------------------------------------------
     ; основная программа
    ORG   0x00
     ; изначально, при инициализации, всегда запрещают все прерывания
    CLRF  INTCON, ab        ; сбросили глобальные флаги разрешения прерываний
    BCF   RCON, IPEN, ab    ; отключаем приоритет
    BRA   Main
     ;---- обработчик прерываний ----
    ORG   0x08
    BTFSS PIR1, CCP1IF, ab        ; прерывание от модуля CCP1?
    BRA   Tmr1_Int                ; нет, проверяем следующий флаг
    BCF   PIR1, CCP1IF, ab        ; да, сбрасываем флаг прерывания от модуля CCP1
    MOVFF CCPR1L, cur_measure0    ; считываем захваченное значение
    MOVFF CCPR1H, cur_measure1    ;
    BTFSC status_reg, first_capture_b, ab    ; первый захват?
    BRA   CCP1_Int_fvalue                     ; нет
    MOVFF cur_measure0, old_measure0         ; да, сохраняем первый захват
    MOVFF cur_measure1, old_measure1         ;
    CLRF CCPR1U, ab                         ; обнуляем страший байт измерений
    CLRF  array_ptr, ab     ; указатель на первый элемент массива усреднения
    BSF   status_reg, first_capture_b, ab  ;ставим признак, что был первый захват
    BRA   Tmr1_Int                           ; проверяем следующее прерывание
CCP1_Int_fvalue:
    LFSR  FSR2, ADDRESS_ACC_ARRAY ;загружаем начальный адрес массива в регистры FSR2
    MOVLW MASK_LENGHT_ARRAY
    ANDWF array_ptr, rw, ab       ; ограничиваем указатель на 32 значения в массиве
    MULLW 3     ; определяем текущий адрес нового значения в массиве(3 байта каждое) 
    MOVF  PRODL, rw, ab                ; FSR2=FSR2+array_ptr*3
    ADDWF FSR2L, rf, ab
    MOVF  PRODH, rw, ab
    ADDWFC FSR2H, rf, ab
    MOVF  old_measure0, rw, ab    ; cur_measure < old_measure ?
    SUBWF cur_measure0, rw, ab    ; проверяем это вычитанием tmp_buf = cur_measure - old_measure
    MOVWF POSTINC2, ab                 ; пишем в массив младший байт результата
    MOVF  old_measure1, rw, ab    ;
    SUBWFB cur_measure1, rw, ab  ;
    MOVWF POSTINC2, ab                 ; пишем в массив второй байт результата
     ; так необходимо делать, т.к. таймер ходит по кольцу
    BTFSS STATUS, C, ab                ; cur_measure < old_measure ? (результат отрицательный ?)
    DECF  CCPR1U, rf, ab               ; да, корректируем старший байт текущего значения периода(CCPR1U - 1)
    MOVFF CCPR1U, INDF2                ; пишем в массив старший байт результата
    MOVFF cur_measure0, old_measure0         ; текущее значение захвата становится старым
    MOVFF cur_measure1, old_measure1         ;
    INCF  array_ptr, rf, ab                  ; указатель на следующий элемент массива
    CLRF  CCPR1U, ab                               ; обнуляем старший байт для нового отсчета
    BSF   status_reg, new_value_b, ab        ; ставим признак, что в массив внесено новое значение
Tmr1_Int:
    BTFSS PIR1, TMR1IF, ab  ; прерывание от таймера 1?
    BRA   Next_Int                ; нет, проверяем дальше
    BCF   PIR1, TMR1IF, ab  ; да, сбрасываем флаг прерывания от таймера 1
    INFSNZ CCPR1U, rf, ab        ; инкрементируем количество кругов таймера 1
    CLRF  status_reg, ab    ; если стал 0, то переполнение, формируем все значения заново
Next_Int:                                ; выход из обработчика прерываний
    RETFIE FAST
     ;---- инициализация микорконтроллера ----
Main:
    MOVLW 0x07
    MOVWF ADCON1, ab        ; отключаем аналоговые входа, все пины цифровые
     ;Все пины настроены  уже на входы автоматически, поэтому TRIS'ы не трогаем
    CLRF  status_reg, ab  ; статусные биты очищаем
    CLRF T3CON, ab         ; источник сигнала таймер 1
     ; Таймер 1 (16-ти битный режим, прескалер 1:1, внтуренняя частота Fosc/4)
     ; т.к. у нас частота кварца 20Мгц
     ; то получится тактирование таймера1=5000000 импульсов в секунду
     ; или 1 тик таймера = 1/5000000=200нсек. В этих тиках и будет представлен результат
     ;т.е. максимальное время, которое мы можем померить 200нсек*256*256*256=3,355сек
    MOVLW 0x81
    MOVWF T1CON, ab               ; включаем таймер 1
    ; включаем нужные прерывания
    CLRF PIR1, ab                ; сбрасываем флаги прерываний
    BSF  PIE1, TMR1IE, ab  ; разрешаем прерывание по переполнению таймера 1
    BSF  PIE1, CCP1IE, ab  ; разрешаем прерывание от модуля CCP1
    BSF  INTCON, PEIE, ab  ; разрешаем периферийные прерывания           
    ; захват по каждому нарастающему фронту входного сигнала на пине RC2(CCP1)
    MOVLW 0x05
    MOVWF CCP1CON, ab       ; включаем модуль CCP1
    Enable_Int                   ; разрешаем прерывания и вызов обработчика                
    ;---- основной цикл микроконтроллера ----                 
Main_Loop:
    CLRWDT      ; сброс сторожевого таймера, обязательно, как минимум, раз в 2,3сек
    BTFSS status_reg, new_value_b, ab  ; в массив внесено новое значение?
    BRA   Main_Loop                     ; нет, ждем, когда это произойдет
    BCF   status_reg, new_value_b, ab  ; обнуляем признак, что в массив внесено новое значение   
    LFSR  FSR0, ADDRESS_ACC_ARRAY ; в регистры FSR0 заносим адрес массива усреднения
    BTFSC status_reg, first_value_b, ab ; первое значение?
    BRA   Main_Loop_Make_Rez      ;нет, тогда переходим к вычислению скользящего среднего
    BSF   status_reg, first_value_b, ab ; устанавливаем признак, что первое значение занесено
    Disable_Int
    MOVFF POSTINC0, rez_reg0      ; первое значение и будет результатом
    MOVFF POSTINC0, rez_reg1      ; читаем его из массива
    MOVFF POSTINC0, rez_reg2      ;
    Enable_Int
    MOVLW MASK_LENGHT_ARRAY       ; заполняем оставшиеся 31 элемент массива
    MOVWF count, ab               ; заносим в счетчик кол-во циклов
Main_Loop_First_val:
    Disable_Int
    MOVFF rez_reg0, POSTINC0      ; заполняем все элементы массива первым значением
    MOVFF rez_reg1, POSTINC0      ;
    MOVFF rez_reg2, POSTINC0      ;
    Enable_Int
    DECFSZ count, rf, ab         ; все элементы заполнили?
    BRA   Main_Loop_First_val     ; нет, тогда продолжаем заполнять
    BRA   Main_Loop               ; да, ждем следующего значения
Main_Loop_Make_Rez:         
    ; выполняем операцию: (сумма всех элементов массива) / 32 (усреднение по 32 значениям)
    CLRF  acc_reg0, ab            ; обнуляем регистр - аккумулятор
    CLRF  acc_reg1, ab
    CLRF  acc_reg2, ab
    CLRF  acc_reg3, ab
    MOVLW MASK_LENGHT_ARRAY+1
    MOVWF count, ab               ; будем складывать 32 значения
Main_Loop_Summ:  
    Disable_Int             ; запрещаем прерывания на период чтения всей переменной
    MOVF  POSTINC0, rw, ab  ;складываем 32 значения массива
    ADDWF acc_reg0, rf, ab 
    MOVF  POSTINC0, rw, ab 
    ADDWFC acc_reg1, rf, ab
    MOVF  POSTINC0, rw, ab 
    Enable_Int             ; разрешаем прерывания
    ADDWFC acc_reg2, rf, ab
    CLRF  WREG, ab
    ADDWFC acc_reg3, rf, ab
    DECFSZ count, rf, ab   ; все элементы сложили?
    BRA   Main_Loop_Summ    ; нет, тогда продолжаем
    MOVLW 0x05
    MOVWF count, ab   ;деление на 32 будем реализовывать с помощью сдвига вправо на 5 разрядов
Main_Loop_Div32: 
    BCF   STATUS, C, ab ;бит переноса обнуляем, т.к. сдвиг будет с учетом переноса 
    RRCF  acc_reg3, rf, ab
    RRCF  acc_reg2, rf, ab
    RRCF  acc_reg1, rf, ab
    RRCF  acc_reg0, rf, ab
    DECFSZ count, rf, ab   ; сдвиги все выполнили?
    BRA   Main_Loop_Div32   ; нет, тогда продолжаем
    ; в младших 3-х байтах регистра - аккумулятора готовый результат
    MOVFF acc_reg0, rez_reg0      ; сохраняем результат
    MOVFF acc_reg1, rez_reg1
    MOVFF acc_reg2, rez_reg2
    BRA   Main_Loop               ; ждем следующего измерения
    END

;==================================================================================

В следующем выпуске продолжим рассматривать модуль захвата/сравнения микроконтроллера PIC18F452.  Начнем рассматривать режим сравнения!

Полный архив рассылки Вы можете прочитать на нашем сайте!  При любом опубликовании текстов данной рассылки ссылка на сайт http://www.2aplusa.ru обязательна!

Желаю Вам удачи! До скорой встречи!


С уважением, Алексей pont_a@mail.ru
Cайт разработчиков 2AplusA http://2aplusa.ru


В избранное