Чтение ШИМ 5 кГц

b707
Offline
Зарегистрирован: 26.05.2017

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

Понравился пример Кактуса, чуть поправил - теперь все работает

uint16_t volatile LL,HL;
int FRQ,DUTY;
void log1(){
    LL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log0,FALLING);
}

void log0(){
    HL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log1,RISING);
}

void setup(){
Serial.begin(9600);
attachInterrupt(0,log1,RISING);
TCCR1A=0;TCCR1B=0;TCNT1=0;
TCCR1B =(1<<CS00)|(1<<CS01);//тик таймера 4мкс
}

void loop(){
int _HL,_LL;
cli();
_HL=HL;
_LL=LL;
sei();
FRQ=250000/(_HL+_LL);
DUTY=(_HL*100)/(_HL+_LL);
Serial.println(FRQ);
Serial.println(DUTY);
delay(1000);
}

Проверка - если добавить в setup()

pinMode(6, OUTPUT);
analogWrite(6,25);

и соединить вывод 6 с INT0 - пином 2, получим в консоли

-1
-1
980
10
984
9
984
9

что точно отражает частоту и скважность в процентах

b707
Offline
Зарегистрирован: 26.05.2017

вдохновленный кодом выше, переделал его на использование прерывания Таймера1 в режиме захвата счетчика:

uint16_t volatile LL,HL;
bool volatile up_edge = true;
int FRQ,DUTY; 
void setup(){
Serial.begin(9600);
TCCR1A=0;TCCR1B=0;TCNT1 = 0;
TCCR1B =(1<<CS00)|(1<<CS01);//тик таймера 4мкс
  //First capture on rising edge
  TCCR1B |= (1 << ICES1);
  //Enable input capture interrupt
  TIMSK1 |= (1 << ICIE1);
  }

ISR(TIMER1_CAPT_vect){
  if (up_edge) {
    LL = ICR1;
    TCCR1B =(1<<CS00)|(1<<CS01);
    up_edge = false;
  }
  else {
    HL = ICR1;
    TCCR1B =(1<<CS00)|(1<<CS01)|(1 << ICES1);
    up_edge = true;
  }
  
  TCNT1 = 0;
}


void loop(){
    int _HL,_LL;
    cli();
    _HL=HL;
    _LL=LL;
    sei();
    FRQ=250000/(_HL+_LL);
    DUTY=(_HL*100)/(_HL+_LL);
    Serial.println(FRQ);
    Serial.println(DUTY);
   delay(1000); 
}

для проверки опять добавлем генерацию ШИМ на пине 6, как в предыдущем случае. Только теперь выход 6 нужно соединять с пином 8 - это вход ICP Таймера 1

Результат:

-1
-1
984
9
984
9
976
10
984
9
976
10
984
9

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Как будто от того, что на форуме будет больше примеров кода, их кто-то начнет читать.

Не надо забывать про атомарность чтения двубайтовые volatile переменных.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:
Не надо забывать про атомарность чтения двубайтовые volatile переменных.

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

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Счётчик - да, а HL и LL?

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:
Счётчик - да, а HL и LL?

и HL и LL тоже, потому что в момент одного прерывания по захвату другое такое же прерывание случится не может - и значит величина ICR1 за время копирования не изменится

sadman41
Offline
Зарегистрирован: 19.10.2016

Хорошо, иначе: в строке 31 гарантирована непрерывность копирования обеих двубайтовые переменных?

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:
Хорошо, иначе: в строке 31 гарантирована непрерывность копирования обеих двубайтовые переменных?

понял.... поправил.

Arcelor55
Offline
Зарегистрирован: 28.04.2020

Всем доброго дня! Я новичок в программировании arduino, по крайней мере таймеры для меня это темный лес. Мне очень помог ваш код,

uint16_t volatile LL,HL;
int FRQ,DUTY;
void log1(){
    LL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log0,FALLING);
}

void log0(){
    HL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log1,RISING);
}

void setup(){
Serial.begin(9600);
attachInterrupt(0,log1,RISING);
TCCR1A=0;TCCR1B=0;TCNT1=0;
TCCR1B =(1<<CS00)|(1<<CS01);//тик таймера 4мкс
}

void loop(){
int _HL,_LL;
cli();
_HL=HL;
_LL=LL;
sei();
FRQ=250000/(_HL+_LL);
DUTY=(_HL*100)/(_HL+_LL);
Serial.println(FRQ);
Serial.println(DUTY);
delay(1000);
}

однако для моей цели мне нужно чтобы считывание ШИМ сигнала производилось не на одном пине как в примере (пин D2) а на двух (D2 и D3). Возможно ли это сделать? Буду очень признателен за ответ.

b707
Offline
Зарегистрирован: 26.05.2017

Arcelor55 пишет:

однако для моей цели мне нужно чтобы считывание ШИМ сигнала производилось не на одном пине как в примере (пин D2) а на двух (D2 и D3). Возможно ли это сделать? Буду очень признателен за ответ.

да, вам сегодня повезло :) - как раз на пинах 2 и 3 это возможно и ни на каких других.

Просто напишите еще одну пару обработчиков , таких же как log0 и log1 - и повесьте их на прерывание  EXT1

Arcelor55
Offline
Зарегистрирован: 28.04.2020

Насколько понял так? Теперь какую то чушь выдаёт на втором пине(( частота со знаком минус, а скважность 50 постоянно.

uint16_t volatile LL,HL,LL1,HL1;
int FRQ,DUTY,FRQ1,DUTY1;
void log1(){
    LL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log0,FALLING);
}

void log0(){
    HL=TCNT1;
    TCNT1=0;
    attachInterrupt(0,log1,RISING);
}
void log3(){
    LL1=TCNT1;
    TCNT1=0;
    attachInterrupt(1,log2,FALLING);
}

void log2(){
    HL1=TCNT1;
    TCNT1=0;
    attachInterrupt(1,log3,RISING);
}
void setup(){
Serial.begin(9600);
attachInterrupt(0,log1,RISING);
attachInterrupt(1,log2,RISING);
TCCR1A=0;TCCR1B=0;TCNT1=0;
TCCR1B =(1<<CS00)|(1<<CS01);//тик таймера 4мкс
}

void loop(){
int _HL,_LL,_HL1,_LL1;
cli();
_HL=HL;
_HL1=HL1;
_LL=LL;
_LL1=LL1;
sei();
FRQ=250000/(_HL+_LL);
FRQ1=250000/(_HL1+_LL1);
DUTY=(_HL*100)/(_HL+_LL);
DUTY1=(_HL1*100)/(_HL1+_LL1);
Serial.println(FRQ);
Serial.println(FRQ1);
Serial.println(DUTY);
Serial.println(DUTY1);
delay(1000);
}

 

Alexander
Offline
Зарегистрирован: 25.04.2010
Kakmyc
Offline
Зарегистрирован: 15.01.2018

b707 пишет:

да, вам сегодня повезло :) - как раз на пинах 2 и 3 это возможно и ни на каких других.

Просто напишите еще одну пару обработчиков , таких же как log0 и log1 - и повесьте их на прерывание  EXT1

Так оно точно работать не будет.
TCNT1 он же общий будет, для 4 функций
Нужно на другом таймере измерялку городить, для двух каналов и точность уже не та будет.

Arcelor55
Offline
Зарегистрирован: 28.04.2020

Ладно, в любом случае спасибо за ответы. Буду писать видимо в рубрику "Ищу исполнителя".

b707
Offline
Зарегистрирован: 26.05.2017

Kakmyc пишет:

Так оно точно работать не будет. TCNT1 он же общий будет, для 4 функций Нужно на другом таймере измерялку городить, для двух каналов и точность уже не та будет.

не нужен второй таймер, достаточно TCNT1 не обнулять и считать разницу междц двумя прерываниями

Kakmyc
Offline
Зарегистрирован: 15.01.2018

b707 пишет:

не нужен второй таймер, достаточно TCNT1 не обнулять и считать разницу междц двумя прерываниями

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

Arcelor55
Offline
Зарегистрирован: 28.04.2020

Мне супер точность не нужна. Может быть сможете подсказать что не так в коде который я выше написал? (Или проще сказать что там так?))

b707
Offline
Зарегистрирован: 26.05.2017

Kakmyc пишет:
b707 пишет:

не нужен второй таймер, достаточно TCNT1 не обнулять и считать разницу междц двумя прерываниями

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

уж не знаю, с чего там должна страдать точность...

Вот такой код

 

uint16_t volatile LL,HL,LL1,HL1;
uint16_t volatile CNT0, CNT1;
int FRQ,DUTY,FRQ1,DUTY1;
void log1(){
    LL= TCNT1 - CNT0;
    CNT0 = TCNT1;
    attachInterrupt(0,log0,FALLING);
}

void log0(){
    HL= TCNT1 - CNT0;
    CNT0 = TCNT1;
    attachInterrupt(0,log1,RISING);
}
void log3(){
    LL1= TCNT1 - CNT1;
    CNT1 = TCNT1;
    attachInterrupt(1,log2,FALLING);
}

void log2(){
    
    HL1= TCNT1 - CNT1;
    CNT1 = TCNT1;
    attachInterrupt(1,log3,RISING);
}
void setup(){
Serial.begin(9600);
pinMode(6, OUTPUT);
analogWrite(6,25);
pinMode(11, OUTPUT);
analogWrite(11,180);
attachInterrupt(0,log1,RISING);
attachInterrupt(1,log3,RISING);
TCCR1A=0;TCCR1B=0;TCNT1=0;
TCCR1B =(1<<CS00)|(1<<CS01);//тик таймера 4мкс
}

void loop(){
uint16_t _HL,_LL,_HL1,_LL1;
cli();
_HL=HL;
_HL1=HL1;
_LL=LL;
_LL1=LL1;
sei();
FRQ=250000/(_HL+_LL);
FRQ1=250000/(_HL1+_LL1);
DUTY=(_HL*100)/(_HL+_LL);
DUTY1=(_HL1*100)/(_HL1+_LL1);
Serial.println(FRQ);
Serial.println(FRQ1);
Serial.println(DUTY);
Serial.println(DUTY1);
delay(1000);
}

если соединить выход PWM пин 6 cо входом EXT0 пин 2, а пин 11 соответвеннос пином 3 - то получаем в Мониторе

976
490
9
70

980
490
9
70

что очень даже неплохо соответвует частоте и заполнению сигнала ШИМ, заданных в setup()

Arcelor55
Offline
Зарегистрирован: 28.04.2020

Спасибо тебе добрый человек!