Модуль захвата (Input Capture) в dsPIC

advertisement
5. Модуль захвата (Input Capture) в dsPIC
5.1. Описание модуля захвата
5.2. Пример использования модуля захвата (пример в PROTEUS)
5.1. Описание модуля захвата
Назначение данного модуля элементарно – т.е. по приходу внешнего
сигнала производить захват значение таймера. Или другими словами,
модуль захвата после внешнего воздействия мгновенно сохраняет в своём
буфере значение, которое находилось в таймере. Режим захвата очень
полезен для измерения периода импульса, длины импульса. Рисунок 1
показывает упрощённую блок-схему модуля захвата.
Рисунок 12.1. Блок схема модуля захвата
Модуль Захвата имеет множество режимов работы, который
выбирается с помощью регистра управления модулем захвата (ICxCON).
Ниже приведены режимы работы модуля захвата:
- Захват по каждому заднему фронту на выводе ICx
- Захват по каждому переднему фронту на выводе ICx
- Захват по каждому 4-му переднему фронту на выводе ICx
- Захват по каждому 16-му переднему фронту на выводе ICx
- Захват по каждому заднему фронту и по каждому переднему фронту на
выводе ICx
- Пробуждение микроконтроллера из режима бездействия по
переднему фронту на выводе ICx
Каждый канал захвата, доступный на dsPIC33 имеет следующие
регистры:
• ICxCON: Регистр управление модулем захвата
• ICxBUF: Буфер модуля захвата
Каждый модуль может выбирать для захвата либо значение таймера
Timer2 либо Timer3. Выбор таймера осуществляется с помощью бита (ICTMR)
в регистре управления модулем захвата (ICxCON <7>). Напомню, что таймеры
могут тактироваться либо внутренней частотой (FOSC/2), либо внешней
частотой, приходящей на вывод TxCK.
Когда происходит условие захвата, то модуль захватывает 16-ти битное
значение выбранного таймера (Timer2 или Timer3). После события захвата
значение из таймера заносится в буфер модуля захвата.
Режим работы модуля захвата выбирается при помощи битов (ICM
<2:0>) в регистре управления модулем захвата (ICxCON <2:0>). Таблица 1
содержит перечень всех режимов работы модуля, а также соответствующие
значение битов. Перед изменением режима работы необходимо данный
модуль отключить (то есть установить биты ICM <2:0> = 000). А после уже
включить для работы в новом режиме.
Таблица 1. Режимы работы модуля захвата
Значение битов
Режим работы модуля захвата
ICM<2:0>
000
Модуль отключён
001
Захват по любому фронту
010
захват после каждого заднего фронта
011
захват после каждого переднего фронта
100
захват после каждого 4-го переднего фронта
101
захват после каждого 16-го переднего фронта
110
не используется (модуль захвата отключён)
111
модуль захвата выводит микроконтроллер из
режима SLEEP и Idle
Рисунок 2 показывает различные способы захвата.
Рисунок 2. Способы захвата
Буфер модуля захвата.
Модуль захвата может захватывать значение или из таймера Timer2
или из Timer3. При каждом случае захвата, захваченное значение таймера
помещается в четырёхуровневый буфер FIFO, как показано на рисунке 3. Для
того чтобы прочитать значение из буфера FIFO предназначен регистр ICxBUF.
Рисунок 3. Захват значения таймера
Для управления буфером FIFO предназначены 2 флага:
• Флаг указывающий, что буфер модуля захвата не пуст (ICBNE)
• Флаг переполнения буфера (ICOV)
Флаг ICBNE (ICxCON <3>) устанавливается при первом случае захвата и
остается установленным, пока все значения из буфера модуля захвата не
будут прочтены. Например, если было произведено три события захвата, то
после 3-х прочтения буфера захвата, флаг ICBNE (ICxCON <3>) будет сброшен
автоматически. Точно так же, если произошло четыре захвата, то 4 раза
прочитав буфер, флаг ICBNE должен будет сброситься.
Флаг ICOV (ICxCON <4>) устанавливается если буфер захвата
переполняется. Когда буфер модуля захвата уже содержит четыре
захваченных значения, и происходит пятый захват до того, как буфер был
прочитан, то происходит переполнение буфера. При этом устанавливается
флаг ICOV (ICxCON <4>). Для того чтобы сбросить данный флаг, необходимо 4
раза прочитать буфер модуля захвата. После четвёртого прочтения флаг ICOV
(ICxCON <4>) сбрасывается. Если буфер модуля захвата полон, то все
последующие захваченные значения теряются.
Прерывание модуля захвата
Модуль захвата может генерировать прерывания, основанные на
выбранном числе событий захвата, как показано не рисунке 4. Число
захватов необходимых для формирования прерывания устанавливаются с
помощью соответствующего бита (ICI <1:0>) в регистре управления модулем
захвата (ICxCON <6:5>), как показано в таблице 2. Перед тем как изменить
режим работы прерывания (биты ICI <1:0>) для модуля захвата, необходимо
отключить модуль захвата (ICM <2:0> = 000).
Если прерывание настроено, чтобы формироваться после каждого
захвата (ICI <1:0> = 00), то флаг переполнения буфера FIFO не запрещает
прерывания модуля захвата. Если прерывание модуля захвата происходит не
после каждого захвата, то флаг переполнения буфера FIFO запретит
прерывание модуля захвата. Поэтому, необходимо при каждом прерывании
модуля захвата полностью читать данные из буфера. Например, если
прерывание происходит после каждых двух захватов (ICI = 01), то буфер
захвата должен прочитаться два раза. Точно так же, если прерывание
генерируется после каждых трёх захватов (ICI = 10), буфер захвата должен
читаться три раза.
Примечание: В режиме работы модуля захвата при захвате на
каждом фронте сигнала (ICM = 001), - биты выбора режима прерывания
(ICI <1:0>) игнорируются.
Таблица 2 Выбор прерывания для модуля захвата
Значение ICI<1:0>
Когда генерируется прерывание модуля захвата
00
Прерывание после каждого захвата
01
Прерывание после двух захватов
10
Прерывание после трёх захватов
11
Прерывание после четырёх захватов
Рисунок 4. Генерирования прерывания модуля захвата
Примечание. Если dsPIC содержит несколько модулей захвата. То для
каждого модуля есть свой бит разрешения прерывания (ICxIE), флаг
прерывании (ICxIF), биты приоритета прерывания (ICxIP)
Пример программы для измерения периода от Microchip
Следующий пример программы реализует измерение периода,
используя модуль захвата. Событие захвата генерируется на каждом
переднем фронте, а прерывание генерируется после двух захватов, как
показано на рисунке 12-5.
Пример 12-1: Пример программы захвата
// Инициализация модуля захвата
IC1CONbits.ICM= 0b00;
// Отключить модуль захвата 1
IC1CONbits.ICTMR= 1;
// Выбрать чтобы модуль захвата IC1 захватывал
значение таймера 2
IC1CONbits.ICI= 0b01;
// Прерывание после каждого второго захвата
IC1CONbits.ICM= 0b011;
// Захват после каждого переднего фронта
// Настраиваем прерывание для модуля захвата 1
IPC0bits.IC1IP = 1;
// Настраиваем уровень приоритета прерывания IC1
IFS0bits.IC1IF = 0;
// Сбрасываем флаг прерывания модуля захвата IC1
IEC0bits.IC1IE = 1;
// разрешаем прерывание IC1
// Обработка прерывания модуля захвата
unsigned int timePeriod= 0;
void __attribute__((__interrupt__)) _IC1Interrupt(void)
{
unsigned int t1,t2;
t2=IC1BUF;
t1=IC1BUF;
IFS0bits.IC1IF=0;
if(t2>t1)
timePeriod = t2-t1;
else
timePeriod = (PR2 - t1) + t2
}
Рисунок 12-5. Пример измерения периода
5.2. Пример использования модуля захвата (пример в PROTEUS)
Для примера мы сделаем небольшую игру, точнее игровой автомат.
Для начала игры нужно нажать на кнопку «Запустить». После этого
светодиоды образуют бегущий огонь. Причём каждый следующий светодиод
будет гореть меньше времени, чем предыдущий. Для остановки бегущего
огня необходимо нажать на кнопку «Остановить». Естественно чем меньше
горит светодиод, тем меньше шансов успеть остановиться именно на нём.
Для игры мы каждому светодиоду присвоим определённое количество очков
по принципу: «чем меньше горит светодиод, тем больше очков можно за
него получить».
Создаём следующую схему в PROTEUS
Итак, принцип работы схемы:
После нажатия на кнопку «Запустить», запускается бегущий
огонь. В программе кнопку «запустить» мы связали с внешним
прерыванием INT2. За текущее положение горящего светодиода
отвечает таймер 3. Весь диапазон данного таймера разделён на
неравномерные участки. В зависимости от текущего участка
загорается определённый светодиод. При переполнении таймера всё
начинается сначала. И так непрерывно до тех пор, пока не нажмём на
кнопку «Остановить». А вот эту кнопку мы связали с модулем захвата
1. В программе мы настроим, чтобы модуль захвата работал в режиме
захвата по КАЖДОМУ заднему фронту (т.е. происходил захват сразу
при нажатии на кнопку). А захват означает, что в регистре IC1BUF,
окажется значение регистра TMR3 (таймер 3). Блокируем бегущий
огонь и выводим то состояние светодиода, при котором была нажата
кнопка. Ну а если что не понятно, то при помощи программы можно во
всём разобраться.
Таймер 2 предназначен для того, чтобы определять моменты,
когда нужно читать значение таймера TMR3 в режиме бегущего огня.
Когда я составлял программу, то попытался отказаться от таймера 2,
однако PROTEUS не успевал моделировать все процессы, и все
действия происходили не в реальном времени, со всеми
вытекающими последствиями. В реальной схеме данный таймер
можно не использовать, программа ещё больше упростится.
На следующем рисунке показана картинка самой игры в
PROTEUS
Далее представлена программа реализации
автомата, используя модуль захвата
данного
игрового
#include "p33Fxxxx.h"
// Устанавливаем биты настройки генератора (HS)
_FOSCSEL(0x02);
_FOSC(0xE2);
unsigned int znachenie;
// Хранит значение таймера
void init (void);
//объявляем подпрограмму инициализации
// ********** подпрограмма инициализация **********
void init (void)
{ _CN11PUE=1;
//включаем подтягивающий резистор на вход CN11
_CN12PUE=1;
//включаем подтягивающий резистор на вход CN12
AD1PCFGL=0xffff;
// все выводы как цифровые I/O
// Инициализация модуля захвата
IC1CONbits.ICM= 0b00;
// Отключить модуль захвата 1
IC1CONbits.ICTMR= 0;
// Выбрать чтобы модуль захвата IC1 захватывал значение таймера 3
IC1CONbits.ICI= 0b00;
// Прерывание после каждого захвата
// Настраиваем прерывание для модуля захвата 1
IPC0bits.IC1IP = 1;
// Настраиваем уровень приоритета прерывания IC1
IFS0bits.IC1IF = 0;
// Сбрасываем флаг прерывания модуля захвата IC1
IEC0bits.IC1IE = 1;
// разрешаем прерывание IC1
// настраиваем таймер, с которого будем захватывать значение
T3CONbits.TON=0; //Таймер 3 выключен,
T3CONbits.TCS=0; //тактовый сигнал внутренний,
T3CONbits.TGATE=0;
// отключаем режим GATE
T3CONbits.TCKPS=0b11; // предделитель устанавливаем 1:256
PR3=0x00FF;
// Заносим значение периода
T3CONbits.TON=1;
//Настроив таймер, его сразу можно запустить
// настраиваем таймер, для периодического опроса таймера TMR3, для организации бегущего огня
T2CONbits.TON=0; //Таймер 3 выключен,
T2CONbits.TCS=0; //тактовый сигнал внутренний,
T2CONbits.TGATE=0;
// отключаем режим GATE
T2CONbits.TCKPS=0b00; // предделитель устанавливаем 1:1
PR2=0x000A;
// Заносим значение периода
PORTB=0;
LATB=0;
TRISB=0xC000;
PORTB=0x0;
//устанавливаем RB15 и RB14 как вход, остальные все выводы на выход
RPINR7=0x000E;
// захватывать будем при нажатии на кнопку "остановить”
// т.е. сигнал берём с вывода RP14
RPINR1=0x000F;
_INT2EP=1;
_INT2IE=1;
// сигнал INT2 настраиваем, чтобы снимать с вывода RP15 (запустить)
// прерывание INT2 происходит по заднему фронту
// разрешаем прерывание INT2
_T2IF=0;
_T2IE=1;
// если был установлен флаг прерывания таймера 2, то сбрасываем его
// разрешаем прерывания таймеру 2
}
// ************ Точка входа в программу *************************
void main (void)
{
init();
// вызываем подпрограмму инициализации
znachenie=0;
// первоначально обнуляем переменную
while(1)
// запускаем бесконечный цикл
{
ClrWdt();
if (znachenie<56)
// Данная конструкция зафиксирует какую-нибудь лампочку
LATB=0x0080;
// горящую, после того как мы нажмём на кнопку "остановка"
else if (znachenie<112)
LATB=0x0040;
else if (znachenie<163)
LATB=0x0020;
else if (znachenie<196)
LATB=0x0010;
else if (znachenie<222)
LATB=0x0008;
else if (znachenie<239)
LATB=0x0004;
else if (znachenie<250)
LATB=0x0002;
else LATB=0x0001;
} // while(1)
}
//***** обработка прерывания INT2 ******* (кнопка запустить)
void __attribute__( (__interrupt__) ) _INT2Interrupt()
{
_T2IF=0;
// Перед запуском таймера, сбросим его флаг прерывания
T2CONbits.TON=1;
//Запускаем таймер
IC1CONbits.ICM= 0b010;
// Захват после каждого заднего фронта
_INT2IF=0;
// сбрасываем флаг прерывания INT2
}
// **** обработка прерывани модуля захвата
void __attribute__((__interrupt__)) _IC1Interrupt(void)
{
T2CONbits.TON=0;
znachenie=IC1BUF;
IC1CONbits.ICM=0b00;
//останавливаем таймер 2, чтобы заморозить значение
// сохраняем захваченное значение
// Отключить модуль захвата 1
IFS0bits.IC1IF=0;
// сбрасываем флаг прерывания модуля IC1
}
// ******* обработка прерывания Таймера 2 ***** (введён, чтобы не тормозил PROTEUS)
void __attribute__( (__interrupt__) ) _T2Interrupt()
{
znachenie=TMR3; // запоминаем значение таймера, чтобы организовать бегущий огонь
_T2IF=0;
// сбрасываем флаг прерывания таймера TMR2
}
Примечания. Данный пример можно было решить и другими методами. Однако считаю, что
данным методом при приходе сигнала с кнопки «Остановить», значение таймера мгновенно
фиксируется, чем, например, если бы нажатие кнопки вызывало прерывание, а после только
сохранялось значение таймера.
Мотькин Игорь Сергеевич,
Республика Беларусь, г. Гомель,
+375 (29) 736-67-41
motskin@tut.by
Download