Частота ШИМ таймера2

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Столкнулся с проблемой. Arduino Nano 328p. На пине D3 таймера2 нужно сделать нестандартную частоту шим в диапазоне 20-25кГц с регулируемой скважностью. Хотелось бы с помощью регистров без использования сторонних библиотек. Помогите советом пожалуйста.

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

Значит имею пин D3 (PD3), на втором таймере это выход OC2B.

В даташите по описанию  таймера 2 увидел такую вот формулу

Так понимаю это как раз расчет необходимой частоты для выхода таймера

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

fclk_I/O - это основная частота (16000000Гц)

fOCnx - частота желаемая

N - значение предделителя

А вот что за величина OCRnx и за что она отвечает не пойму.

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

В статье то же самое, но ещё и с примерами расчётов.

Что-то мне кажется, что простым способом именно 20-25кГц не получить.

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

Vlad1m1r пишет:

А вот что за величина OCRnx и за что она отвечает не пойму.

Максимальное число, которое можно в OCR2A/OCR2B поместить. Таймер восьмибитный, значит OCRnx = 255.

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

Vlad1m1r пишет:

А вот что за величина OCRnx и за что она отвечает не пойму.

Это то значение (0-255), которое Вы засунете в регистр OCR2A/B - что засунете, то там и будет.  А как это значение влияет на частоту, Вы видите из формулы.  Прочитайте абзац, в котором формула, целиком, там всё написано.

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019
Прерывание может генерироваться каждый раз, когда значение счетчика достигает значения TOP с помощью флага OCF2A. Если прерывание включено, процедура обработки прерываний может использоваться для обновления значения TOP.
Однако изменение значения TOP на значение, близкое к BOTTOM, когда счетчик работает без значения none или с низким значением precaler, должно быть выполнено с осторожностью, так как в режиме CTC отсутствует функция двойной буферизации.
Если новое значение, записанное в OCR2A, ниже текущего значения TCNT2, счетчик пропустит сравнение. После этого счетчик должен будет подсчитать максимальное значение (0xFF) и обернуть его, начиная с 0x00, прежде чем может произойти сравнение.
Для генерации выходного сигнала формы сигнала в режиме CTC OC2A выходной сигнал может быть установлен для переключения его логического уровня при каждом сравнении путем установки битов режима сравнения выходных сигналов в режим переключения (COM2A [1:0] = 1). Значение OC2A не будет отображаться на контакте порта, если не задано направление вывода данных для контакта. Генерируемый сигнал будет иметь максимальную частоту fOC2A = fclk_I/O/2, когда OCR2A установлен в нуль.
(0x00). Частота сигнала определяется следующим уравнением:
�OCnx =
�clk_I/O
2-1 ОКРнх
Переменная N представляет предкалейный коэффициент (1, 8, 32, 64, 128, 256 или 1024).
Что касается обычного режима работы, флаг TOV2 устанавливается в том же такте таймера, который счетчик подсчитывает от MAX до 0x00.
 
С учетом какого-то корявого перевода, как-то много неясности. И в начале я не обозначил, что мне нужен режим с фазокоррекцией.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

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

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

С английским согласен, особенно с техническим. Без него прям туговато

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Значит в регистрах TCCR2A и TCCR2B с помощью битов WGM выберу режим фазокоррекции. Битами CS выберу значение прескалера

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

В регистр OCR2B накатываю нужное значение для нужной частоты, например 155 OCR2B = 0b10011011

 

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

Vlad1m1r пишет:
в начале я не обозначил, что мне нужен режим с фазокоррекцией.

в статье, на которую вам дали ссылку в первом ответе - есть готовый пример запуска Phase-Correct ШИМ, причем именно на Таймере2. Все что остается - это разобраться, какой прескалер поставить. Возьмите да переведите ее сами, там всего-то 2-3 странички, заодно и в английском попрактикуетесь.

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

Vlad1m1r пишет:

В регистр OCR2B накатываю нужное значение для нужной частоты, например 155 OCR2B = 0b10011011

 

сосершенно необязательно переводить значение в бинарный вид, запись  OCR2B  = 155 ничем не хуже. А еще значения счетчиков можно задавать с помощью обычных analogWrite() - эта инструкция не модифицирует TCCR2A и TCCR2B

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

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

 

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Кто-нибудь не сможет объяснить на двух пальцах смысл этих строчек, не могу вникнуть:

  • Сравните выходные данные режима A ( COMnA ): они включают/отключают/инвертируют выходные данные A
  • Сравнить выходные биты режима B ( COMnB ): они включают/отключают/инвертируют выходные B
b707
Offline
Зарегистрирован: 26.05.2017

Vlad1m1r пишет:

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

 

это правильно, но знать, что свои настройки TCCR2A TCCR2B  и стандартная функция analogWrite() совместимы - бывает полезно.

И потом, я ж на аналогВрайте не настаиваю, ты в суть вниикай, а не к деталям цепляйся :)

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

Vlad1m1r пишет:

Кто-нибудь не сможет объяснить на двух пальцах смысл этих строчек, не могу вникнуть:

  • Сравните выходные данные режима A ( COMnA ): они включают/отключают/инвертируют выходные данные A
  • Сравнить выходные биты режима B ( COMnB ): они включают/отключают/инвертируют выходные B

вы продолжаете пользоваться кривым переводом? - читайте оригинал...

при совпадении COMnA == OCRnA  происходит включение/выключение/инверсия выхода канала А таймера n

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Про аналогВрайт, я не зацепился, просто подметил, что вот пишут такое)

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

В этой таблице интересный режим под номером 5. Так полагаю, что в OCR2A записываем необходимое значение для получения необходимой частоты. А сам ШИМ сможем снимать только с пина B

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

Vlad1m1r пишет:
натыкаюсь на такое высказывание, что аналогВрайт инструкция достаточно медленная и нужно прибегать к регистрам для увеличения скорости работы
А какая скорость Вам нужна? В реальности analogWrite медленный в весьма небольшом количстве специальных случаев, для 99% применений его скорости более, чем достаточно. Вселенский плач на тему медленности чаще всего поднимают те, кому особая скорость-то и не нужна, просто они не умеют пользоваться регистрами и им кажется, что вот "если через регистры сделать, то все мои проблемы, включая некомпиляцию из-за отсутствующей точки с запятой, сразу же по волшебству разрешатся".

А по всему остальному, да сделайте же Вы, наконец, что я Вам говорил - запуститесь на железе или на модели и пощупайте руками / посмотрите глазами. Чего теоретизировать, искать проблемы на ровном месте, и пугать себя ими?

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Если я потенциометром буду регулировать скважность частота тоже будет соответственно плавать? Или я ошибаюсь? Или я вообще ничего не понимаю)))

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

Vlad1m1r пишет:

Если я потенциометром буду регулировать скважность частота тоже будет соответственно плавать?

не будет

 

Цитата:

Или я вообще ничего не понимаю)))

похоже на то. Может все-таки прочитать , как работает ШИМ?

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Прочитать это одно. А вот с пониманием почему-то проблема

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

Vlad1m1r пишет:

Прочитать это одно. А вот с пониманием почему-то проблема

Так я ж Вам и говорю, запустите и щупайте руками!

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Евгений, для запуска такой скетч будет годен???

#define pwmPin 3

void setup(){
TCCR2A = 0;	//	обнуляю регистр
TCCR2B = 0;	//	обнуляю регистр
TCCR2A |= (1 << WGM20);	//	режим фазовой коррекции №1
TCCR2B |= (1 << CS21);	//	прескаллер /8
OCR2B = 44;	//	f_OC2B (22222Гц) = 16000000Гц / 2 * 8 (1 + 44)
}

void loop(){
pot = analogRead(A0);
pot1 = map(pot, 0, 1023, 0, 255);
analogWrite(pwmPin, pot1);
}

 

 

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Есть осцилл DSO138mini. Им можно будет частоту на этом пине померить

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

Vlad1m1r пишет:

Евгений, для запуска такой скетч будет годен???

Запустите и узнаете.

Vlad1m1r пишет:

Есть осцилл DSO138mini. Им можно будет частоту на этом пине померить

Смотря какую частоту Вы настроите. У 138, емнип один мегасэмпл в сек. и полоса пропускания до 200 кГц. Т.е. разумную частоту можно, вроде Клапауций этого пока не запрещал.

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

uint16_t pot;
uint8_t pot1;

void setup(){
DDRD |= (1 << PD3);							//	D3 на выход
TCCR2A = 0;									//	обнуляю регистр
TCCR2A |= (1 << COM2B1) | (1 << WGM20));	//	сброс при нарастании, установка на убыванию  | режим фазовой коррекции №5
TCCR2B = 0;									//	обнуляю регистр
TCCR2B |= (1 << WGM22) | (1 << CS21);		//	режим фазовой коррекции №5 | прескаллер /8 
OCR2A = 44;									//	f_OC2B (22222Гц) = 16000000Гц / (2 * 8 (1 + 44))
}

void loop(){
pot = analogRead(A0);
pot1 = map(pot, 0, 1023, 0, 255);
pot1 = constrain(pot1, 0 , 255);
OCR2B = pot1;
}

 

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

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

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

Vlad1m1r, вы в курсе, что будет, когда OCR2B станет > OCR2A ?

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Счётчик начнет считать вниз, я так полагаю

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

Vlad1m1r, именно вниз. В Mode5 счётный регистр TCNT2 никогда не будет больше, чем OCR2A,  а регистр OCR2B тоже сравнивает своё содержимое с регистром счёта TCNT2. Соотвентссно при значениях OCR2B > (TCNT2=OCR2A) совпадения никогда не произойдёт, и выход таймера останется в состоянии по-умолчанию. Т.е. диапазон регулировки  скважности в вашем случае будет от 0 ... до  44. 

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Это как-то лечится?

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

Vlad1m1r, лучше сделать всё это на  16-битном таймере(1) , либо если это невозможно, есть вариант  поиграться с системным прескалером (CLKPR) . Тактовую 16MHz можно поделить  на 2, т.е. сделать 8Мгц,  а таймером тактовую уже не нужно не делить. Получим счёт до (OCR2A)=   ((16000000/2) / 22000)/2   ~ 150, что вполне хватит для регулировки скважности регистром OCR2B

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Использование системного прескаллера также скажется и на остальных таймерах?

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

Vlad1m1r, да, скажется на всём.

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

http://arduino.ru/forum/apparatnye-voprosy/pomoshch-po-apparatnoi-zashchite. В этой ветке я изначально изложил схему управления коллекторным двигателем. Нюанс в том, что у tlp250 граничная частота по даташиту 25к

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

А диапазон меньше 20к не устраивает по шуму

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Поэтому надо попасть в диапазон по таймеру2 20-25к

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

На таймере1 у меня реализован счётчик оборотов по Вашему коду, он был в ветке по управлению двигателями. Код полный сейчас не выложу, т.к. с телефона

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019
#define obMin 200           //  минимальные обороты двигателя
#define obMax 6000          //  максимальные обороты двигателя
#define kImp 8              //  количество импульсов на 1 оборот таходатчика. 6 магнитов - три пары полюсов, соответственно kImp 3
#define PWM 3               //  пин выхода ШИМ D3

uint16_t poten;             //  переменная регулятора скорости (потенциометра)
uint16_t pokazPoten;        //  показания регулятора скорости (потенциометра)
uint32_t RPM;           //  переменная оборотов
uint8_t pRim;  //  переменная ШИМ
uint32_t last_time;         //  переменная для замены delay

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

volatile uint16_t int_tic;
volatile uint32_t tic;

void setup() {
  lcd.init(); // Turn on the blacklight and print a message.
  lcd.backlight();
  lcd.clear();

  
  DDRD = (1 << PD3);  //  установить D3 на выход
  // Пины D3 и D11 - 31.4 кГц
  TCCR2B = 0b00000001; // x8
  TCCR2A = 0b00000001; // phase correct + (1 << COM2B1)

  DDRD &= ~(1 << PD2); //  установить D2 на вход
  //настройка режима прерывания INT0
  EICRA = 0;              //  обнулил регистр настройки прерывания
  EICRA |= (1 << ISC01);  //  прерывание по INT0 при смене HIGH на LOW
  EIMSK = 0;              //  обнулил регистр включения прерываний
  EIMSK |= (1 << INTF0);  //  включил прерывание на INT0 (пин D2)

  //pinMode(8, INPUT);
  DDRB &= ~(1 << PB0); // вход сигнала ICP( D8 only для atmega328)
  //настройка 16 бит таймера-счётчика 1
  TCCR1B = 0;
  TCCR1A = 0;
  TCNT1 = 0;
  TIMSK1 = (1 << ICIE1) | (1 << TOIE1); //создавать прерывание от сигнала на пине ICP1
  TCCR1B = (1 << ICNC1) | (1 << ICES1) | (1 << CS10); //div 1
}


ISR(INT0_vect) { // процедура обработки прерывания по внешнему прерыванию 0 пин D2, защита
  //TCCR2A = (1 << COM2B1);
  //OCR2B = 0; // установить на D3 шим = 0
}


ISR (TIMER1_CAPT_vect) { //прерывание захвата сигнала на входе ICP1
  TCNT1 = 0;
  if (TIFR1 & (1 << TOV1)) {
    TIFR1 |= 1 << TOV1;
    if (ICR1 < 100) {
      int_tic++;
    }
  }
  tic = ((uint32_t)int_tic << 16) | ICR1 ; //подсчёт тиков
  int_tic = 0;
}

ISR(TIMER1_OVF_vect) {  //  прерывание для счёта по переполнению uint
  int_tic++;            //  считать переполнения через 65536 тактов
  if (int_tic > 244) {  //  если на входе пусто более секунды
    tic = 0;            //  то обнулить счётчики
    int_tic = 0;
  }
}

void loop() {
  //  Расчет оборотов
  RPM = (((float)F_CPU / tic) * 60) / kImp;
  /* tic-количество тактов процессора, совершенных за 1 период входного сигнала.
    если tic*(1/F_CPU) то будет время периода в секундах.
    если F_CPU/tic - будет частота в герцах. Как пример: Hz = (float)F_CPU / tic;
  */

  //  Регулирование
  poten = analogRead(A0);                         //  потенциометр на пине А0
  pokazPoten = map(poten, 0, 1023, obMin, obMax); //  Приводим показания регулятора к минимальным и максимальным оборотам
  if (poten > 0) {                                //  если регулятор больше 0
    if (RPM > pokazPoten) {                       //  если реальные обороты больше нужных
      pRim = pRim - 1;                            //  то от значения ШИМ отнимаем 1
      pRim = constrain(pRim, 0, 255);             //  Следим чтоб ШИМ был не меньше 0 и не больше 255
      analogWrite(PWM, pRim);                   //  Выводим значение ШИМ на двигатель
      //TCCR2A |= (1 << COM2B1);
      //OCR2B = pRim;                               // установить на D3 шим = pRim
    }
    else {
      pRim = pRim + 1;                            //  то к значению ШИМ + 1
      pRim = constrain(pRim, 0, 255);             //  Следим чтоб ШИМ был не меньше 0 и не больше 255
      analogWrite(PWM, pRim);                   //  Выводим значение ШИМ на двигатель
      //TCCR2A |= (1 << COM2B1);
      //OCR2B = pRim;                               // установить на D3 шим = pRim
    }
  }
  else {
    pRim = 0;                                     //  Если регулятор на 0 то значение ШИМ = 0
    analogWrite(PWM, pRim);                     //  Выводим значение ШИМ на двигатель
    //TCCR2A |= (1 << COM2B1);
    //OCR2B = pRim;                                 // установить на D3 шим = pRim
  }

  //  отображение инфы
  if (millis() - last_time > 200) {
    last_time = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ob/min:");
    lcd.print(RPM);
    lcd.setCursor(0, 1);
    lcd.print(pRim);
    lcd.setCursor(10, 1);
    lcd.print(poten);
  }
}

 

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

Вот полный код

Vlad1m1r
Offline
Зарегистрирован: 08.06.2019

скажется ли негативно изменение прескаллера системной частоты на подсчет оборотов в этом конкретном случае?