Подружить прерывание по таймеру и вывод на индикатор

Buldakov
Offline
Зарегистрирован: 17.01.2016

Необходимо получить:

Вызов прерывания по таймеру 2000 раз в секунду для опроса аналогового входа. Опрос идет непрерывно. Каждую секунду необходимо выводить на экран количество прерываний по таймеру и количество тактов таймера 2.

В коде приведенном ниже все работает. Прерывание вызывается 1000 раз в секунду и обновление экрана происходит раз в секунду. Количество тактов, которое считает таймер 2 равно 250000.

#include <Wire.h>                                                                   
#include <LiquidCrystal_I2C.h>                                                     
LiquidCrystal_I2C lcd(0x27, 16, 2);      // Для экрана 16х2 (двухстрочный)         
//------------------------------------------------------------------------
unsigned long interval_izm     =  1000;  //Время смены показаний на LCD (миллисекунд) 
//------------------------------------------------------------------------------------
unsigned long ovf_tic_timer_2;                                                        
unsigned long tik_timer_2;                                                            
boolean       flag_LED,sig_Lcd;                                                       
//------------------------------------------------------------------------------------
void setup() {                                                                        
lcd.init();                              //Задаем размерность LCD дисплея             
lcd.backlight();                         //Включаем подсветку экрана                  
lcd.clear();                             //Очистка экрана                             
//---Настройка таймера 2--------------------------------------------------------------
TCCR2A=0; TCCR2B=0; TCNT2=0;                                                          
TCCR2B  = 1<<CS22;                        //делитель CLK/64                           
OCR2A   = 249;                                                                        
TIMSK2 |= (1 << OCIE2A);                 //enable timer compare interrupt             
//------------------------------------------------------------------------------------
}                                                                                     
//---Прерывание по таймеру 2----------------------------------------------------------
ISR (TIMER2_COMPA_vect){                                                              
ovf_tic_timer_2=ovf_tic_timer_2+1; //считать количество переполнений таймера 2        
}                                                                                     
//------------------------------------------------------------------------------------
void loop()                                                                           
{                                                                                     
//------------------------------------------------------------------------------------
tik_timer_2=(unsigned long)ovf_tic_timer_2*250+TCNT2;                                 
//---Условия для вывода показаний-----------------------------------------------------
if (tik_timer_2>=(interval_izm*250)) sig_Lcd=1;                                       
//------------------------------------------------------------------------------------
if (sig_Lcd==1)                                //Условия вывода                       
{                                                                                     
//---Вывод показаний на экран---------------------------------------------------------
lcd.clear();                                   //Очистка экрана                       
lcd.setCursor(0, 0);lcd.print(ovf_tic_timer_2);                                       
lcd.setCursor(0 ,1);                                                                  
lcd.print(tik_timer_2);                                                               
//------------------------------------------------------------------------------------
sig_Lcd=0;                                                                            
ovf_tic_timer_2=0;tik_timer_2=0;                                                      
flag_LED=0;                              //Сигнал об окончании вывода на индикатор   
}                                                                                    
//-----------------------------------------------------------------------------------
}                                                                                    

Теперь пытаюсь сделать  вызов прерывания 2000 раз в секунду и вывод на экран раз в секунду.

меняю строку 18 на : OCR2A   = 124;

строку 30 на : tik_timer_2=(unsigned long)ovf_tic_timer_2*125+TCNT2;                                 
 

При этом количество прерываний равно 2000 и количество тактов 250000, как и положено,Но обновление экрана происходит 1 раз в 2 секунды.

Если 18 и 30 строки меняю на числа 64 и 65 соответственно - то вывод на экран 1 раз в 4 секунды.

Где мог сделать ошибку. Ошибка простая, но непойму где?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Думаю, что ошибка на стадии проектирования.

По условию задачи требуется выводить на экран раз в секунду вне зависимости от прерываний. Вот и не нужно к ним привязываться. Сделайте аналогично blink without delay.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Buldakov пишет:

2000 раз в секунду для опроса аналогового входа

Не то, чтобы это невозможно. Возможно, конечно, но, надеюсь, Вы знаете, что делаете.

Период получается 500 микросекунд, а длительность чтения с аналогового входа (при полном разрешении) - 104 микросекунды.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Надо 2000 раз в секунду непрерывно и через равные промежутки времени считывать аналоговый вход. Далее значение аналогового входа усредняем тоже 2000 раз в секунду и среднее значение 1 раз в секунду выводим на индикатор.Это краткая задачка.

Полная задачка того, что надо.

По схеме:

Выход D10 через резистор в 100 К подключен к А0.

А0 через резистор в 1000К подключен к А1.

А1 подключен к выходу D9.

Паралельно резистору в 1000К подключено неизвесное сопротивление - величина которого зависит от знака приложенного напряжения и времени неизвесным образом. Необходимо менять полярность ны выводах D9,D10 так,  чтобы интегралы измеряемого сопротивления для разной полярности были равны по величине и противоположны по знаку. Для этого необходимо измерять значение сопротивления для каждой полярности, с переключением этой полярности. (считывать аналоговый вход и планируется  2000 раз в сек) Для интегрирования нужны точные интервалы времени. И раз в секунду выводим на экран значение среднего сопротивления и ассиметрии значения сопротивления.

Или более просто по электрической части: Нужно сделать мультивибратор с частотой от 2 до 30 Гц. На RC цепочке. Частота зависит от величины сопротивления. Чем меньше сопротивление - тем ниже частота. Сопротивление зависит от знака напряжения , величины приложенного напряжения, и времени.

По повода времени преобразования 110 мкс знаю. Поэтому и выбрал частоту 2кгц.

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нерадивым скудентам тут помогают за деньги. 

Buldakov
Offline
Зарегистрирован: 17.01.2016

Ну знаете-?

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Buldakov, лень вникать глубоко в ваш код, но сходу глаз режет 2 ошибки. Возможно дело и не в них, но дебажить без устранения их этих ошибок точно нет смысла.

unsigned long ovf_tic_timer_2;    

Если вы работаете с переменной, которая меняется в прерывании, вне этого прерывания -то эта переменная должна быть объявлена как volatile

void loop()                                                                          
{                                                                                    
//------------------------------------------------------------------------------------
tik_timer_2=(unsigned long)ovf_tic_timer_2*250+TCNT2;

Если эта переменная >8 бит, то перед любой операцией с ней нужно запретить прерывания, иначе переменная может измениться прямо в момент её чтения, что грозит существеными ошибками.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Спасибо. Не знал. Попробую.

Результат поменялся. Если раньше было 1999 раз прерывание за 2 секунды. То после замены переменной результат стал 2003.

Попробовал вместо 2 таймера сделать на таймере 1. На первом таймере все работает. Вероятно что то в настройках второго таймера. Пока единственную вижу разницу в 3 и 8 строчках. Есть подозрение, что вместо запуска таймера я указал делитель. Тогда непонятно, как запустить и таймер и коэффициент деления отличный от единицы.

//---Настройка таймера 1--------------------------------------------------------------
TCCR1A  = 0; TCCR1B  = 0; TCNT1   = 0;                                                
TCCR1B=(1<<CS10)|(1<<WGM12);                   //Запускаем Таймер 1                   
OCR1A   = 7999;                    /отсчитать 8 000 импульсов до прерывания таймера 
TIMSK1 |= (1 << OCIE1A);                       //enable timer compare interrupt      
//---Настройка таймера 2--------------------------------------------------------------
TCCR2A=0; TCCR2B=0; TCNT2=0;                                                          
TCCR2B  = 1<<CS22;                        //делитель CLK/64                           
OCR2A   = 124;                                                                        
TIMSK2 |= (1 << OCIE2A);                 //enable timer compare interrupt             
//------------------------------------------------------------------------------------

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Buldakov, всё таки вынудили углубится :) Резюме таково -программа работает в точности так, как вы её написали. От того, что вы меняете OCR2 меняется только фаза выпрыгивания прерывания относительно перехода счётчика TCNT2 через ноль. Частота прерываний в данном режиме у вас не меняется, и фиксирована  концом счёта 8-битного счётчика. Короче ставьте режим CTC, и всё заработает.

Buldakov
Offline
Зарегистрирован: 17.01.2016

Просьба глянуть. Вроде код победил.

Было

//---Настройка таймера 2--------------------------------------------------------------
TCCR2A=0; TCCR2B=0; TCNT2=0;                                                          
TCCR2B  = 1<<CS22;                        //делитель CLK/64                           
OCR2A   = 124;                                                                        
TIMSK2 |= (1 << OCIE2A);                 //enable timer compare interrupt             

Стало: и вроде работает.  Запутался с регистрами 2А и 2В. Забыл добавить регистр 2А. Вернее добавил и обнулил значение.

TCCR2A = (0<<COM2A1)|(1<<COM2A0)|(1<<WGM21)|(0<<WGM20); //CTC mode 
TCCR2B  = 1<<CS22;          //Коэффициент предделителя 64                         
OCR2A =124;                 //Регистр сравнения. Сюда мы записываем то, с чем надо сравнить.  
TIMSK2 |= (1 << OCIE2A);    //enable timer compare interrupt