Частота ШИМ таймера2
- Войдите на сайт для отправки комментариев
Ср, 21/08/2019 - 09:58
Столкнулся с проблемой. Arduino Nano 328p. На пине D3 таймера2 нужно сделать нестандартную частоту шим в диапазоне 20-25кГц с регулируемой скважностью. Хотелось бы с помощью регистров без использования сторонних библиотек. Помогите советом пожалуйста.
Помогаю: https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
Значит имею пин D3 (PD3), на втором таймере это выход OC2B.
В даташите по описанию таймера 2 увидел такую вот формулу
Так понимаю это как раз расчет необходимой частоты для выхода таймера
fclk_I/O - это основная частота (16000000Гц)
fOCnx - частота желаемая
N - значение предделителя
А вот что за величина OCRnx и за что она отвечает не пойму.
В статье то же самое, но ещё и с примерами расчётов.
Что-то мне кажется, что простым способом именно 20-25кГц не получить.
А вот что за величина OCRnx и за что она отвечает не пойму.
Максимальное число, которое можно в OCR2A/OCR2B поместить. Таймер восьмибитный, значит OCRnx = 255.
А вот что за величина OCRnx и за что она отвечает не пойму.
Это то значение (0-255), которое Вы засунете в регистр OCR2A/B - что засунете, то там и будет. А как это значение влияет на частоту, Вы видите из формулы. Прочитайте абзац, в котором формула, целиком, там всё написано.
Ну, можно и не приводить здесь гугл перевод. Вам же сказали, что это такое. Теперь Вы можете попробовать на железе или протеусной модели разные значения, померять частоту и впредь знать.
Кстати, английский надо прокачивать. Без него в этом хобби человек - инвалид на всю голову.
С английским согласен, особенно с техническим. Без него прям туговато
Значит в регистрах TCCR2A и TCCR2B с помощью битов WGM выберу режим фазокоррекции. Битами CS выберу значение прескалера
В регистр OCR2B накатываю нужное значение для нужной частоты, например 155 OCR2B = 0b10011011
в статье, на которую вам дали ссылку в первом ответе - есть готовый пример запуска Phase-Correct ШИМ, причем именно на Таймере2. Все что остается - это разобраться, какой прескалер поставить. Возьмите да переведите ее сами, там всего-то 2-3 странички, заодно и в английском попрактикуетесь.
В регистр OCR2B накатываю нужное значение для нужной частоты, например 155 OCR2B = 0b10011011
сосершенно необязательно переводить значение в бинарный вид, запись OCR2B = 155 ничем не хуже. А еще значения счетчиков можно задавать с помощью обычных analogWrite() - эта инструкция не модифицирует TCCR2A и TCCR2B
По форуму часто серфинг делаю, в поисках ответов на свои вопросы. И часто натыкаюсь на такое высказывание, что аналогВрайт инструкция достаточно медленная и нужно прибегать к регистрам для увеличения скорости работы
Кто-нибудь не сможет объяснить на двух пальцах смысл этих строчек, не могу вникнуть:
По форуму часто серфинг делаю, в поисках ответов на свои вопросы. И часто натыкаюсь на такое высказывание, что аналогВрайт инструкция достаточно медленная и нужно прибегать к регистрам для увеличения скорости работы
это правильно, но знать, что свои настройки TCCR2A TCCR2B и стандартная функция analogWrite() совместимы - бывает полезно.
И потом, я ж на аналогВрайте не настаиваю, ты в суть вниикай, а не к деталям цепляйся :)
Кто-нибудь не сможет объяснить на двух пальцах смысл этих строчек, не могу вникнуть:
вы продолжаете пользоваться кривым переводом? - читайте оригинал...
при совпадении COMnA == OCRnA происходит включение/выключение/инверсия выхода канала А таймера n
Про аналогВрайт, я не зацепился, просто подметил, что вот пишут такое)
В этой таблице интересный режим под номером 5. Так полагаю, что в OCR2A записываем необходимое значение для получения необходимой частоты. А сам ШИМ сможем снимать только с пина B
А по всему остальному, да сделайте же Вы, наконец, что я Вам говорил - запуститесь на железе или на модели и пощупайте руками / посмотрите глазами. Чего теоретизировать, искать проблемы на ровном месте, и пугать себя ими?
Если я потенциометром буду регулировать скважность частота тоже будет соответственно плавать? Или я ошибаюсь? Или я вообще ничего не понимаю)))
Если я потенциометром буду регулировать скважность частота тоже будет соответственно плавать?
не будет
Или я вообще ничего не понимаю)))
похоже на то. Может все-таки прочитать , как работает ШИМ?
Прочитать это одно. А вот с пониманием почему-то проблема
Прочитать это одно. А вот с пониманием почему-то проблема
Так я ж Вам и говорю, запустите и щупайте руками!
Евгений, для запуска такой скетч будет годен???
#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); }Есть осцилл DSO138mini. Им можно будет частоту на этом пине померить
Евгений, для запуска такой скетч будет годен???
Запустите и узнаете.
Есть осцилл DSO138mini. Им можно будет частоту на этом пине померить
Смотря какую частоту Вы настроите. У 138, емнип один мегасэмпл в сек. и полоса пропускания до 200 кГц. Т.е. разумную частоту можно, вроде Клапауций этого пока не запрещал.
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; }Вчерашние изыскания привели к такому варианту кода. При этом, изменяя скважность в OCR2B, заданная частота по формуле остается на одном значении
Vlad1m1r, вы в курсе, что будет, когда OCR2B станет > OCR2A ?
Счётчик начнет считать вниз, я так полагаю
Vlad1m1r, именно вниз. В Mode5 счётный регистр TCNT2 никогда не будет больше, чем OCR2A, а регистр OCR2B тоже сравнивает своё содержимое с регистром счёта TCNT2. Соотвентссно при значениях OCR2B > (TCNT2=OCR2A) совпадения никогда не произойдёт, и выход таймера останется в состоянии по-умолчанию. Т.е. диапазон регулировки скважности в вашем случае будет от 0 ... до 44.
Это как-то лечится?
Vlad1m1r, лучше сделать всё это на 16-битном таймере(1) , либо если это невозможно, есть вариант поиграться с системным прескалером (CLKPR) . Тактовую 16MHz можно поделить на 2, т.е. сделать 8Мгц, а таймером тактовую уже не нужно не делить. Получим счёт до (OCR2A)= ((16000000/2) / 22000)/2 ~ 150, что вполне хватит для регулировки скважности регистром OCR2B
Использование системного прескаллера также скажется и на остальных таймерах?
Vlad1m1r, да, скажется на всём.
http://arduino.ru/forum/apparatnye-voprosy/pomoshch-po-apparatnoi-zashchite. В этой ветке я изначально изложил схему управления коллекторным двигателем. Нюанс в том, что у tlp250 граничная частота по даташиту 25к
А диапазон меньше 20к не устраивает по шуму
Поэтому надо попасть в диапазон по таймеру2 20-25к
На таймере1 у меня реализован счётчик оборотов по Вашему коду, он был в ветке по управлению двигателями. Код полный сейчас не выложу, т.к. с телефона
#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); } }Вот полный код
скажется ли негативно изменение прескаллера системной частоты на подсчет оборотов в этом конкретном случае?