Перейти к содержанию
    

Таймеры и прерывания

Здравствуйте!

В программировании микроконтроллеров новичок, поэтому прошу не ругать сразу)

Встала задача:

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

Длительность импульсов - 5 мс, амплитуда - 12 В.

Пока что продумываю алгоритм.

Как я понимаю, надо настроить один из таймеров на запуск по входному нарастающему импульсу. Тут вопрос - остановится ли таймер по приходу следующего импульса, чтобы можно было считать накопленное им значение?

Заранее благодарю за помощь.

 

Здравствуйте!

В программировании микроконтроллеров новичок, поэтому прошу не ругать сразу)

Встала задача:

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

Длительность импульсов - 5 мс, амплитуда - 12 В.

Пока что продумываю алгоритм.

Как я понимаю, надо настроить один из таймеров на запуск по входному нарастающему импульсу. Тут вопрос - остановится ли таймер по приходу следующего импульса, чтобы можно было считать накопленное им значение?

Заранее благодарю за помощь.

Расстояние между импульсами от 10 до 200 мс.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы не указали тип МК, который предполагается использовать.

Если AVR, то в таймерах есть (не всех, но - есть) "Input Capture": при определенном изменении уровня сигнала сохраняется значение счетчика таймера - легко вычислить длительность импульса.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы не указали тип МК, который предполагается использовать.

Если AVR, то в таймерах есть (не всех, но - есть) "Input Capture": при определенном изменении уровня сигнала сохраняется значение счетчика таймера - легко вычислить длительность импульса.

Спасибо)

Тип МК - AtMega88. Частота работы - 16 Мгц.

Посмотрел, вроде бы есть в нем такая функция.

То есть таймер запоминает время прихода внешних прерываний?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

То есть таймер запоминает время прихода внешних прерываний?
Да, для этого используется вход таймера ICP. На эту ногу нужно завести Ваш сигнал (предварительно преобразовав его к диапазону 0...Vcc).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, для этого используется вход таймера ICP. На эту ногу нужно завести Ваш сигнал (предварительно преобразовав его к диапазону 0...Vcc).

Спасибо большое.

Спросить больше не у кого. Не подскажете ли вы, какие средства наиболее простыми будут для освоения и решения задач написания программы для микроконтроллера, моделирования, получения hex файла.

Желательно чтобы было как можно более наглядно.

Для обучения на примерах я использовал CodeVision + Proteus.

Нет ли у Вас примера кода программы по использованию input capture? Желательно в си)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Могу посоветовать "средство" - Winavr, или его нынешнее развитие - AVR Toolchain. Это компилятор, IDE к нему каждый выбирает по себе. Я, например, использую CodeBlocks.

Еще одно необходимое "средство" - документация (datasheet) на используемый МК. Если Вы планируете и дальше заниматьсся решением подобных задач, то это должно стать вашей "настольной книгой" первое время.

По поводу примера программы: любую задачу можно попробовать начать решать с составления алгоритма. В данном случае, это можно представить так:

1. Настройка захвата - выбор типа фронта, вкл. или выкл. шумоподавителя, прерываний (если нужно)

2. Настройка соответствующего таймера (выбор режима и т.д.)

3. Сброс флага захвата

4. Включение захвата

5. Ожидание флага захвата, старт таймера.

6. Сброс флага захвата

7. Ожидание флага захвата, чтение результата.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Могу посоветовать "средство" - Winavr, или его нынешнее развитие - AVR Toolchain. Это компилятор, IDE к нему каждый выбирает по себе. Я, например, использую CodeBlocks.

Еще одно необходимое "средство" - документация (datasheet) на используемый МК. Если Вы планируете и дальше заниматьсся решением подобных задач, то это должно стать вашей "настольной книгой" первое время.

По поводу примера программы: любую задачу можно попробовать начать решать с составления алгоритма. В данном случае, это можно представить так:

1. Настройка захвата - выбор типа фронта, вкл. или выкл. шумоподавителя, прерываний (если нужно)

2. Настройка соответствующего таймера (выбор режима и т.д.)

3. Сброс флага захвата

4. Включение захвата

5. Ожидание флага захвата, старт таймера.

6. Сброс флага захвата

7. Ожидание флага захвата, чтение результата.

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

Спасибо за советы!

 

Для начала пытаюсь настроить прерывание по захвату, настроил таймер. Просто увеличиваю переменную по таймеру, вывожу ее на lcd.

Сделал модель в протеусе, но что то не выводит ничего на индикатор. Буду благодарен если кто то подскажет, что я делаю не так.


#include <mega88.h>

 

// Alphanumeric LCD Module functions

#include <alcd.h>

unsigned int numb;

// Timer1 input capture interrupt service routine

interrupt [TIM1_CAPT] void timer1_capt_isr(void)

{

TCNT1H=0;

TCNT1L=0;

numb++;

}

void main(void)

{

// Crystal Oscillator division factor: 1

#pragma optsize-

CLKPR=0x80;

CLKPR=0x00;

#ifdef _OPTIMIZE_SIZE_

#pragma optsize+

#endif

 

// Input/Output Ports initialization

// Port B initialization

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T

PORTB=0x00;

DDRB=0x00;

 

// Port C initialization

// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In

// State6=T State5=T State4=T State3=T State2=T State1=T State0=T

PORTC=0x00;

DDRC=0x00;

 

// Port D initialization

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T

PORTD=0x00;

DDRD=0x00;

 

 

TCCR0A=0x00;

TCCR0B=0x00;

TCNT0=0x00;

OCR0A=0x00;

OCR0B=0x00;

 

 

TCCR1A=0x00;

TCCR1B=0x43;

TCNT1H=0x00;

TCNT1L=0x00;

ICR1H=0x00;

ICR1L=0x00;

OCR1AH=0x00;

OCR1AL=0x00;

OCR1BH=0x00;

OCR1BL=0x00;

 

// Timer/Counter 2 initialization

// Clock source: System Clock

// Clock value: Timer2 Stopped

// Mode: Normal top=0xFF

// OC2A output: Disconnected

// OC2B output: Disconnected

ASSR=0x00;

TCCR2A=0x00;

TCCR2B=0x00;

TCNT2=0x00;

OCR2A=0x00;

OCR2B=0x00;

 

// External Interrupt(s) initialization

// INT0: Off

// INT1: Off

// Interrupt on any change on pins PCINT0-7: Off

// Interrupt on any change on pins PCINT8-14: Off

// Interrupt on any change on pins PCINT16-23: Off

EICRA=0x00;

EIMSK=0x00;

PCICR=0x00;

 

// Timer/Counter 0 Interrupt(s) initialization

TIMSK0=0x00;

 

// Timer/Counter 1 Interrupt(s) initialization

TIMSK1=0x20;

 

// Timer/Counter 2 Interrupt(s) initialization

TIMSK2=0x00;

 

// USART initialization

// USART disabled

UCSR0B=0x00;

 

// Analog Comparator initialization

// Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80;

ADCSRB=0x00;

DIDR1=0x00;

 

// ADC initialization

// ADC disabled

ADCSRA=0x00;

 

// SPI initialization

// SPI disabled

SPCR=0x00;

 

// TWI initialization

// TWI disabled

TWCR=0x00;

 

// Alphanumeric LCD initialization

// Connections specified in the

// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:

// RS - PORTD Bit 0

// RD - PORTD Bit 1

// EN - PORTD Bit 2

// D4 - PORTD Bit 4

// D5 - PORTD Bit 5

// D6 - PORTD Bit 6

// D7 - PORTD Bit 7

// Characters/line: 16

lcd_init(16);

 

// Global enable interrupts

#asm("sei")

 

while (1)

{

{

lcd_gotoxy(0,0);

lcd_putchar(numb/10+0x30);

lcd_putchar(numb%10+0x30);

}

}

}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Программу переработал, снова пытаюсь в протеусе посмотреть.

На экране lcd появляется какой то значок, только не то, что мне надо.

Вот думаю в чем проблема. Может переменные надо переводить где то из одной системы исчисления в другую.

Или Fuse выставлять.

Кстати, почему то в протеусовской модели Atmega88 нельзя поставить тактовую частоту 16 МГц.

Добавлю код, посмотрите пожалуйста, кому не трудно.

#include <mega88.h>
// Подключение библиотеки LCD модуля
#include <alcd.h>

//Объявление переменных
unsigned int EdgeR, EdgeF;//время первого и второго импульса
unsigned long PulseClocks;//количество тактовых импульсов внутри импульса на входе ICP

// Прерывание таймера 1 по событию захват на входе PB0
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
static unsigned char a;//локальная статическая переменная
    if(!a)                 //если чётный вход в обработчик по нарастающему фронту (новое измерение периода)
    {
        EdgeR = ICR1L;      //запоминаем значение счётчика
        a = 1;             //изменяем переменную для завершения текущего измерения
    }
    else                    //если нечётный вход в обработчик по нарастающему фронту (завершение текущего измерения)
    {
        EdgeF = ICR1L;       //запоминаем значение счётчика
        a = 0;              //изменяем переменную для нового измерения периода
        PulseClocks = ((unsigned long)EdgeF - (unsigned long)EdgeR);//вычисляем период
    }

}

void main(void)
{
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=P State6=P State5=P State4=P State3=P State2=P State1=P State0=P 
PORTB=0xFF;
DDRB=0x00;

// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTD=0x00;
DDRD=0x00;


// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 250,000 kHz
// Input Capture on Rising Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: On
TCCR1A=0x00;
TCCR1B=0x43;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x20;

lcd_init(16);

// Global enable interrupts
#asm("sei")

while (1)
      {
      
      
      // выводим значение переменной на экран
      lcd_gotoxy(0,0);  
      lcd_putchar(PulseClocks);
      }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На экране lcd появляется какой то значок, только не то, что мне надо.

Вы выдаёте на LCD байт со значением периода в "тиках" таймера следования импульсов, а не последовательность символов, соответствующему этому периоду (естественно, требуется преобразование - самое простое, но не эффективное: использовать процедуру sprintf).

 

посмотрите пожалуйста, кому не трудно.

1. Читаете, почему-то, только младший байт регистра

        EdgeR = ICR1L;      //запоминаем значение счётчика
.....
        EdgeF = ICR1L;       //запоминаем значение счётчика

Нужно, примерно так:

        EdgeR = ICR1;      //запоминаем значение счётчика
.....
        EdgeF = ICR1;       //запоминаем значение счётчика

2. Таймер считает с частотой 250кГц, а период следования импульсов 0.2 с. Т.е. за максимальный период у Вас получается 50 000 "тиков" таймера... В переменную можно поместить такое число? Нет...

3. Между импульсами таймер может досчитать до максимального значения и сбросить свои счетчики TCNT в ноль, и начать счет дальше. Эту ситуацию (переполнение счетчиков) тоже нужно учитывать/отслеживать (нужна процедура обработки прерывания по переполнению) и учитывать при вычислении периода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы выдаёте на LCD байт со значением периода в "тиках" таймера следования импульсов, а не последовательность символов, соответствующему этому периоду (естественно, требуется преобразование - самое простое, но не эффективное: использовать процедуру sprintf).

 

 

1. Читаете, почему-то, только младший байт регистра

        EdgeR = ICR1L;      //запоминаем значение счётчика
.....
        EdgeF = ICR1L;       //запоминаем значение счётчика

Нужно, примерно так:

        EdgeR = ICR1;      //запоминаем значение счётчика
.....
        EdgeF = ICR1;       //запоминаем значение счётчика

2. Таймер считает с частотой 250кГц, а период следования импульсов 0.2 с. Т.е. за максимальный период у Вас получается 50 000 "тиков" таймера... В переменную можно поместить такое число? Нет...

3. Между импульсами таймер может досчитать до максимального значения и сбросить свои счетчики TCNT в ноль, и начать счет дальше. Эту ситуацию (переполнение счетчиков) тоже нужно учитывать/отслеживать (нужна процедура обработки прерывания по переполнению) и учитывать при вычислении периода.

Спасибо.

Но мне то как раз нужно, чтобы на дисплей выводилось значение переменной в тиках таймера 250 кГц)

1. Когда я выставляю ICR1, CodeVision ругается на эту строку undefined symbol.

2. Значит надо использовать переменную long?

3. да, про это я подумал, но думал, что мне хватает разрядности таймера. Теперь понял свою ошибку.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но мне то как раз нужно, чтобы на дисплей выводилось значение переменной в тиках таймера 250 кГц)
Пусть, например, период в тиках таймера получился 85. Если Вы выдадите байт с этим значением как символ(байт) на LCD, то на экране увидите символ "U" (если я не ошибаюсь). Вам же надо вывести (в данном примере) два символа "8" и "5". Такое преобразование из числа в последовательность символов производит функция sprintf (бывают и другие функции для такого преобразования).

 

Когда я выставляю ICR1, CodeVision ругается на эту строку undefined symbol.
В некоторых трансляторах двухбайтный регистр ICR1 определён. В Вашем случае можно сделать так:

EdgeR = ICR1L;
EdgeR= EdgeR | (ICR1H << 8);

Значит надо использовать переменную long?
Вероятно. Или уменьшить частоту таймера. В любом случае нужно предусмотреть ситуацию, когда импульсы следуют через большой интервал времени - хорошая программа при этом должна отображать что-то "внятное"...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...