Программирование таймера 0

Olm
Offline
Зарегистрирован: 09.10.2014

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

void setup() {                
  pinMode(13, OUTPUT);    


  OCR0A =125; //регистр совпадения
  //Timer in CTC mode (TCCR0A)
  TCCR0B =(1<<CS00)|(1<<CS01); // Тактировать с делителем 64
   TIMSK0|=(1<<OCIE0A); //  Разрешить прерывание по совпадению
  // turn on CTC mode:CTC (сброс при совпадении)Clear Timer on Compare (CTC) установкой бит WGMn1 в TCCRn. 
 //При этом счётчик после совпадения с регистром сравнения будет сбрасываться автоматически.
  TCCR0A |= (1 << WGM01);
 
 
  sei();                //бит общего разрешения прерываний


}


void loop() {
}


//ОБРАБОТЧИК ПРЕРЫВАНИЙ ПО ТАЙМЕРУ

ISR(TIMER0_COMPA_vect) {
//TCNT0 = 0;// счетный регистр

  
if (iiii==1000){
digitalWrite(13,!digitalRead (13)); iiii=0;}
iiii++;
}

Чувствую что где-то туплю, а где непонятно, ткните носом если кто знает. Нулевой таймер под ардуиной решил использовать так как не буду использовать функции delay, millis, и pwm

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Всё вроде правильно, но не объявлена переменная iiii. Atmel Studio такой код даже не компилирует.

volatile int iiii;

Объявил переменную, прогнал код через отладчик в студии - всё работает. Происходит прерывание по совпадению с OCR0A.

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

Olm, Вы тупите в 11 строке :) Записываете бит, видимо подразумевая, что регистр TCCR0A=0. Откуда такая уверенность?  Делайте как в 7 строке. Всё что не указано  - сбросится в ноль.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

dimax пишет:

Записываете бит, видимо подразумевая, что регистр TCCR0A=0. Откуда такая уверенность? 

В даташите написано Initial Value 0 0 0 0 0 0 0 0, т.е. в теории TCCR0A=0. Но на практике черт его знает.....

Я ненужные мне биты никогда не сбрасываю специально в ноль, всё и так работает. Как по даташиту. Мож от конкретного экземпляра МК зависит.

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

Jeka_M,  Лучше сбрасывать :)) Дуняша свой порядок наводит, тем более timer0 её любимчик  :)

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Спасибо за такие нюансы, буду знать.

Olm
Offline
Зарегистрирован: 09.10.2014

Ничего что-то не помогает. Прерывание наступает вовремя, но регистр TCNT0 не обнуляется автоматически по совпадению и продолжает тикать до переполнения. Только если его вручную обнулять в обработчике прерывания. Но в даташите сказано что в режиме CTC должен счетчик сбрасываться автоматически при совпадении с регистром сравнения

Olm
Offline
Зарегистрирован: 09.10.2014

Я всё это дело из среды ардуино компилирую и прошиваю, может она что-то меняет?

Olm
Offline
Зарегистрирован: 09.10.2014

В общем заработало. Регистры TCCR0A и TCCR0B надо сначала грузить, чтоб работали прерывания по совпадению.

И еще пожалуй лучше обнулить перед разрешением прерываний счетный регистр TCNT0.

volatile int iiii;
void setup() {                
  pinMode(13, OUTPUT);    
//  TCCR0A=B00000010;
//  TCCR0B =B00000011;
    TCCR0A = (1 << WGM01);// режим CTC
    TCCR0B =(1<<CS00)|(1<<CS01); // Тактировать с делителем 64
    OCR0A =125; //регистр совпадения
    TIMSK0|=(1<<OCIE0A); //  Разрешить прерывание по совпадению
    sei();                //бит общего разрешения прерываний
}

void loop() {
}


//ОБРАБОТЧИК ПРЕРЫВАНИЙ ПО ТАЙМЕРУ

ISR(TIMER0_COMPA_vect) {
//TCNT0 = 0;// счетный регистр
if (iiii==1000){
digitalWrite(13,!digitalRead (13)); iiii=0;}
iiii++;
}

 

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

Olm, по идее стартовой "кнопкой" таймера  является запись клоков. Желательно их первой же строкой обнулить, потом сперва сконфигурировать все другие регистры, и в конце дать нужные клоки (7 строка) , тогда всё должно стартовать правильно.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Еще перед запуском таймера (CS01, CS00) можно обнулять "буфер" предделителя (бит PSRSYNC в регистре GTCCR). Т.к. даже когда таймер остановлен - предделитель постоянно тикает.

Но надо быть осторожным, если используются два таймера (Timer0 и Timer1), т.к. "буфер" предделителя у них общий. Будет сбрасываться у обоих.

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Здравствуйте написал код использующий таймер 0 для подсчета количество переполнений для борьбы с дребезгом и прерывание по изменению состояния вывода PCINT в данном случае с кнопкой на выводе РВ0 подтянутой внутренним резистором к питанию.

[Плата pro mini на mega168, памяти не особо много остается и такой вариант кода опроса на регистровом уровне считаю оптимальным в плане расхода памяти.]

Тестовый светодиод должен инвертироваться по каждому нажатию 

Дребезг часто проскакивает, в чем может быть причина?

#include <avr/io.h>
#include <avr/interrupt.h>

static volatile uint32_t ovfCount = 0;
static volatile boolean flag = 0;

#define BOUNCE_DELAY    50

static uint32_t lastINTtime0 = 0;  // для первой кнопки
static uint32_t lastINTtime1 = 0;  // для второй кнопки

void timer_init(void)
{
   cli();//stop interrupts
   TCCR0A = 0;// set entire TCCR0A register to 0
   TCCR0B = 0;// same for TCCR0B
   TCNT0  = 0;//initialize counter value to 0
   //Устанавливаем источник тактов -> 1/(16000000/64)* 256 = 1 мс
   TCCR0B =(1<<CS00)|(1<<CS01); // Тактировать с делителем 64
   TIMSK0 |= _BV(TOIE0); // разрешаем прерывание по переполнению
    //разрешаем прерывания
    sei();
   
}

ISR (TIMER0_OVF_vect)
{
  ovfCount++;
}

uint32_t getOverflowCount(void)
{
    return ovfCount;
}

ISR (PCINT0_vect)
{
    /* interrupt code here */   
   uint32_t currINTtime = getOverflowCount();
    if ((currINTtime - lastINTtime0) > BOUNCE_DELAY)
    {
        lastINTtime0 = currINTtime;
        if( !(PINB & (1 << PINB0))) PORTB ^= (1 << PB5);
        // main operation
    }
}

int main(void)
{  
    
   DDRB |= (1 << PB5);      // set output led
   DDRB &= ~(1 << PB0);     // Clear the PB0 pin
   // PB0 (PCINT0 pin) is now an input

   PORTB &= ~(1 << PB5);    // turn Off led
   PORTB |= (1 << PB0);    // turn On the Pull-up
   // PB0 is now an input with pull-up enabled

   PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
   PCMSK0 |= (1 << PCINT0);  // set PCINT0 to trigger an interrupt on state change 

   timer_init();
    //sei();                    // turn on interrupts

   while(1) {};
}

ПС: подскажите если не трудно как перейти к setup() и loop() Возникает конфликт с delayMicroseconds

C:\Users\Alex\AppData\Local\Temp\build4998539872587939610.tmp/core.a(wiring.c.o): In function `delayMicroseconds':
E:\Arduino uno R3\Р?Рљ станция\arduino-1.6.0\hardware\arduino\avr\cores\arduino/wiring.c:49: multiple definition of `__vector_16'
C:\Users\Alex\AppData\Local\Temp\build4998539872587939610.tmp\sketch_jun24a.cpp.o:/arduino-1.6.0/sketch_jun24a.ino:27: first defined here
collect2: error: ld returned 1 exit status
Ошибка компиляции.
// Ошибка компиляции этого кода
#include <avr/io.h>
#include <avr/interrupt.h>

static volatile uint32_t ovfCount = 0;
static volatile boolean flag = 0;

#define BOUNCE_DELAY    50

static uint32_t lastINTtime0 = 0;  // для первой кнопки
static uint32_t lastINTtime1 = 0;  // для второй кнопки

void timer_init(void)
{
   cli();//stop interrupts
   TCCR0A = 0;// set entire TCCR0A register to 0
   TCCR0B = 0;// same for TCCR0B
   TCNT0  = 0;//initialize counter value to 0
   //Устанавливаем источник тактов -> 1/(16000000/64)* 256 = 1 мс
   TCCR0B =(1<<CS00)|(1<<CS01); // Тактировать с делителем 64
   TIMSK0 |= _BV(TOIE0); // разрешаем прерывание по переполнению
    //разрешаем прерывания
    sei();
   
}

ISR (TIMER0_OVF_vect)
{
  ovfCount++;
}

uint32_t getOverflowCount(void)
{
    return ovfCount;
}

ISR (PCINT0_vect)
{
    /* interrupt code here */   
   uint32_t currINTtime = getOverflowCount();
    if ((currINTtime - lastINTtime0) > BOUNCE_DELAY)
    {
        lastINTtime0 = currINTtime;
        if( !(PINB & (1 << PINB0))) PORTB ^= (1 << PB5);
        // main operation
    }
}

//int main(void)
void setup() 
{  
    
   DDRB |= (1 << PB5);      // set output led
   DDRB &= ~(1 << PB0);     // Clear the PB0 pin
   // PB0 (PCINT0 pin) is now an input

   PORTB &= ~(1 << PB5);    // turn Off led
   PORTB |= (1 << PB0);    // turn On the Pull-up
   // PB0 is now an input with pull-up enabled

   PCICR |= (1 << PCIE0);    // set PCIE0 to enable PCMSK0 scan
   PCMSK0 |= (1 << PCINT0);  // set PCINT0 to trigger an interrupt on state change 

   timer_init();
    //sei();                    // turn on interrupts

   //while(1) {};
}

void loop() {}

 

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

BuonanotteMasha пишет:

Дребезг часто проскакивает, в чем может быть причина?

Плохой алгоритм. Дребезг возникает как при нажатии кнопки, так и при отпускании её. У вас прерывание срабатывает и на нажатие и на отпускание, и счёт времени запускается и так и так, в результате получается что и алгоритм срабатывает и на нажатие и на отпускание, и  ваше условие if( !(PINB & (1 << PINB0))) легко срабатывает от  дребезга, а не от нажатия кнопки. Короче аглоритм негодный.

BuonanotteMasha пишет:

ПС: подскажите если не трудно как перейти к setup() и loop() Возникает конфликт с delayMicroseconds

Разумеется, таймер0 - системный таймер ардуины, который по умолчанию занят. Так что либо не используёте таймер0, либо не используйте функцию setup(), которая прогружает все настройки по умолчанию.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

dimax, спасибо. Понял свою ошибку и решил несколько упростить код - убрал прерывание по таймеру 0. Подавление дребезга произвожу аппаратно - параллельно каждой кнопке поставил керамический конденсатор на 100 нФ. Дребезг пропал.

#include <avr/io.h>
#include <avr/interrupt.h>     // Необходимо использовать прерывания    

volatile uint8_t flag = 0;

int main(void)
{
    DDRB |= (1 << PB5);      // set output led
    DDRB &= ~(1 << PB0);         // Clear the PB0 pin

    PORTB |= (1 << PB0);        // turn On the Pull-up
    PORTB &= ~(1 << PB5);    // turn Off led

    PCICR |= (1 << PCIE0);     // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);   // set PCINT0 to trigger an interrupt on state change 

    sei();                     // turn on interrupts

    while(1){}
}

ISR (PCINT0_vect)
{
    if( !(PINB & (1 << PB0)) == 1)  { PORTB ^= (1 << PB5); }
}