Увеличение частоты PWM по всем 6-ти пинам

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Не нашёл ответа на форуме, в основном обсуждается разрядность PWM более 8 бит, задача иная нужно Шимить 6 пинов с увеличением частоты, можно  с дискретностью позволяемой делителями Таймеров, разрядность 8 бит.

Реализуемо?

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

Да, кто ж не даёт-то? Если просто нужно (типа из звуковых частот уйти), так в статье про разрядность у меня и таблицы частот есть. Если же хочется гибко управлять частотой, так там же таймеры в три строки конфигурируются (см. #12 в той же статье). Да и вроде библиотека была типа PWM.h

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Да, кто ж не даёт-то? Если просто нужно (типа из звуковых частот уйти), так в статье про разрядность у меня и таблицы частот есть. Если же хочется гибко управлять частотой, так там же таймеры в три строки конфигурируются (см. #12 в той же статье). Да и вроде библиотека была типа PWM.h

так читал я и даже про проблему 0 и 255 значения, мне надо синхронно, на всех шести пинах, именно частоту а не разрядность, по  режиму FAST PWM получается для Таймера 2 использовать делители из ряда Таймера 0,1, по коду не понял )))

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

Так там же написано как частоту менять.

Насчёт синхронности раньше речи не было. Целиком синхронно? Или скважность может отличаться? Насколько синхронно? Какое расхождение допустимо?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Так там же написано как частоту менять.

Насчёт синхронности раньше речи не было. Целиком синхронно? Или скважность может отличаться? Насколько синхронно? Какое расхождение допустимо?

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

Так настраиваю таймера:

 TCCR1A |= _BV(COM1A1);  // connect pin 9 to timer 1 channel A  
    TCCR1A |= _BV(COM1B1);  // connect pin 10 to timer 1 channel B
       TCCR2A |= _BV(COM2A1);  // connect pin 11 to timer 2 channel A
...

Так загружаю нужным значением PWM

OCR1A =pwm1;
 OCR1B =pwm2;
  OCR2A =pwm3;
...

или хотя бы по четырём каналам )))
 

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

Ну, а в чём тогда проблема? Настраивайте все 6 так - в чём беда-то? Я думал Вам надо суперсинхронность, хотел с этим поэкспериментировать.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Ну, а в чём тогда проблема? Настраивайте все 6 так - в чём беда-то? Я думал Вам надо суперсинхронность, хотел с этим поэкспериментировать.

супер синхронность желательна (даже очень желательна) по двум каналам )))

А частоту ШИМ - просто загружая соответствующие предделители?

 

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

Конечно.

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

ua6em пишет:

супер синхронность желательна (даже очень желательна) по двум каналам )))

А по двум каналам она всегда будет абсолютно синхронна, если каналы от одного таймера. Проблема заставить все таймеры синхронно работать.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

супер синхронность желательна (даже очень желательна) по двум каналам )))

А по двум каналам она всегда будет абсолютно синхронна, если каналы от одного таймера. Проблема заставить все таймеры синхронно работать.

если есть возможность попробовать по четырём, буду признателен!!!

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

ua6em пишет:

если есть возможность попробовать по четырём, буду признателен!!!

возьми СТМ-ку, там таймеры 4х канальные

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

b707 пишет:

ua6em пишет:

если есть возможность попробовать по четырём, буду признателен!!!

возьми СТМ-ку, там таймеры 4х канальные

ограничен 328 чипом )))

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

Ну, я попробую, но не сейчас. Может, завтра.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Ну, я попробую, но не сейчас. Может, завтра.

мне абсолютно не срочно и по шиму, если можно 1 килогерц и 7-8 килогерц

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Только учти, что изменение частоты ШИМ на пинах связанных с Timer0 приведет к неправильной работе delay и millis. Если какие из используемых библиотек завязаны на это, то придется их править.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

asam пишет:

Только учти, что изменение частоты ШИМ на пинах связанных с Timer0 приведет к неправильной работе delay и millis. Если какие из используемых библиотек завязаны на это, то придется их править.

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

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

Ну, в общем, я не понял в чём у Вас затык.

Настраивается таймер на частоту PWM просто подбором нужного делителя.

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

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

Вот пример на приблизительно 1 кГц и приблизительно 8 кГц (для 8 кГц закомментируйте строку №1)

#define	ONE_KHZ	

//
//	Делители таймеров на 1 кГц 
//
#ifdef	ONE_KHZ //	на самом деле 980Гц
	constexpr byte prescaler0 = bit(CS00) | bit(CS01);
	constexpr byte prescaler1 = bit(CS10) | bit(CS11);
	constexpr byte prescaler2 = bit(CS22);
//
//	Делители таймеров на 8 кГц 
//
#else	// 8кГц (на самом деле 7812.5 Гц)
	constexpr byte prescaler0 = bit(CS01);
	constexpr byte prescaler1 = bit(CS11);
	constexpr byte prescaler2 = bit(CS21);
#endif

//
//	Настройка таймера 0 на FastPWM
//
void setupTimer0(void) {
	TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
	TCCR0B = prescaler0;
	TCNT0 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR0A = 80;	//	уставнока скважности
	OCR0B = 150;	//	уставнока скважности
	TIMSK0 = 0;
}

//
//	Настройка таймера 1 на FastPWM
//
void setupTimer1(void) {
	TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM10);
	TCCR1B =  bit(WGM12) | prescaler1;
	TCNT1 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR1A = 60;	//	уставнока скважности
	OCR1B = 200;	//	уставнока скважности
	TIMSK1 = 0;
}

//
//	Настройка таймера 2 на FastPWM
//
void setupTimer2(void) {
	TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
	TCCR2B = prescaler2;
	TCNT2 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR2A = 85;	//	уставнока скважности
	OCR2B = 170;	//	уставнока скважности
	TIMSK2 = 0;
}

void setup(void) {
	Serial.begin(57600);
	Serial.print("Fun is here");
	pinMode(3, OUTPUT);
	pinMode(11, OUTPUT);
	pinMode(9, OUTPUT);
	pinMode(10, OUTPUT);
	pinMode(5, OUTPUT);
	pinMode(6, OUTPUT);
	//
	//	Обесточить ВСЕ таймеры
	//
	PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
	//
	setupTimer0();
	setupTimer1();
	setupTimer2();
	//
	//	Запустить ВСЕ таймеры одной командой процессора
	//
	PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
}

void loop(void){}
dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

В меге есть специальные регистр, который даёт возможность синхронизировать сразу всё таймеры


void setup() {
pinMode (11,OUTPUT); 
pinMode (9,OUTPUT); 
pinMode (6,OUTPUT); 
GTCCR=(1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC); 

TCNT0=0;
TIMSK0=0;
TCCR0A=(1<<COM0A1)|(1<<WGM00)|(1<<WGM01);
TCCR0B=(1<<CS00);
OCR0A=127;


TCNT1=0;
TIMSK1=0;
TCCR1A=(1<<WGM10)|(1<<COM1A1);
TCCR1B=(1<<WGM12)|(0<<WGM13)|(1<<CS10); 
ICR1=0; 
OCR1A=127; 

TCNT2=0;
TIMSK2=0;
TCCR2A=(1<<COM2A1)|(1<<WGM20)|(1<<WGM21);
TCCR2B=(1<<CS20);
OCR2A=127;


GTCCR=0;


}
void loop() {}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Благодарю! Сегодня поэкспериментирую!

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

dimax пишет:

В меге есть специальные регистр, который даёт возможность синхронизировать сразу всё таймеры


void setup() {
pinMode (11,OUTPUT); 
pinMode (9,OUTPUT); 
pinMode (6,OUTPUT); 
GTCCR=(1<<TSM)|(1<<PSRASY)|(1<<PSRSYNC); 

TCNT0=0;
TIMSK0=0;
TCCR0A=(1<<COM0A1)|(1<<WGM00)|(1<<WGM01);
TCCR0B=(1<<CS00);
OCR0A=127;


TCNT1=0;
TIMSK1=0;
TCCR1A=(1<<WGM10)|(1<<COM1A1);
TCCR1B=(1<<WGM12)|(0<<WGM13)|(1<<CS10); 
ICR1=0; 
OCR1A=127; 

TCNT2=0;
TIMSK2=0;
TCCR2A=(1<<COM2A1)|(1<<WGM20)|(1<<WGM21);
TCCR2B=(1<<CS20);
OCR2A=127;


GTCCR=0;


}
void loop() {}

 

О меге я думал, тем более, что там два процессора, на 32U4 реализовать разбор протокола обмена, а на самой меге всё остальное

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

OCR1A = 60 и пр. OCRnA - установка частоты.

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

ua6em пишет:

О меге я думал

Видимо, dmax имел в виду ATmega, т.к. в 328 такое тоже есть - сейчас посмотрел. Не пробовал, но в даташите написано.

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

dimax пишет:

есть специальные регистр, который даёт возможность синхронизировать сразу всё таймеры

Спасибо, буду знать.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:
OCR1A = 60 и пр. OCRnA - установка частоты.

может скважности?

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

sadman41 пишет:
OCR1A = 60 и пр. OCRnA - установка частоты.
С какого перепугу?

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

А, да... Сорри. Фокус с TOP в OCRxA - это при PWM с фазовой коррекцией. Там OCRxA определяет частоту, а OCRxB - скважность. Сегодня только всю голову изломал этим.

PS, ан нет - Fast PWM-у тоже можно сделать произвольный TOP и задавать частоту: https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM -> Fast PWM Mode with OCRA top. Што-то меня этот PWM запутал опять, надо пойти поспать.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

всё получается

sadman41
Offline
Зарегистрирован: 19.10.2016
Вызывает интерес вот такой еще разрез:
 
The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR® CPU via the 8-bit data bus. The 16-bit register must be byte accessed using two read or write operations. 
...
It is important to notice that accessing 16-bit registers are atomic operations. If an interrupt occurs between the two instructions accessing the 16-bit register, and the interrupt code updates the temporary register by accessing the same or any other of the 16-bit timer registers, then the result of the access outside the interrupt will be corrupted. 
 
Когда там начинает TCNT тикать, например... Надо ли обязательно операции доступа в ATOMIC заворачивать на всякий пожарный (чтобы привычку выработать)?
 
В ДШ даже пример имеется:
void TIM16_WriteTCNT1(unsigned int i) {
   unsigned char sreg;
   unsigned int i;
   /* Save global interrupt flag */
   sreg = SREG;
   /* Disable interrupts */
   _CLI();
   /* Set TCNT1 to i */
   TCNT1 = i;
   /* Restore global interrupt flag */
   SREG = sreg;
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

dimax пишет:

есть специальные регистр, который даёт возможность синхронизировать сразу всё таймеры

Спасибо, буду знать.

может имеет смысл в базу знаний по ШИМ на сайт добавить?

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

Может и имеет, но в принципе ... даташит надо внимательнее читать :)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Может и имеет, но в принципе ... даташит надо внимательнее читать :)

Там столько букв, столько букв, а чтобы разобраться и запомнилось, надо проверять на пробных скетчах...
 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Вызывает интерес вот такой еще разрез:
 
The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR® CPU via the 8-bit data bus. The 16-bit register must be byte accessed using two read or write operations. 
...
It is important to notice that accessing 16-bit registers are atomic operations. If an interrupt occurs between the two instructions accessing the 16-bit register, and the interrupt code updates the temporary register by accessing the same or any other of the 16-bit timer registers, then the result of the access outside the interrupt will be corrupted. 
 
Когда там начинает TCNT тикать, например... Надо ли обязательно операции доступа в ATOMIC заворачивать на всякий пожарный (чтобы привычку выработать)?
 
В ДШ даже пример имеется:
void TIM16_WriteTCNT1(unsigned int i) {
   unsigned char sreg;
   unsigned int i;
   /* Save global interrupt flag */
   sreg = SREG;
   /* Disable interrupts */
   _CLI();
   /* Set TCNT1 to i */
   TCNT1 = i;
   /* Restore global interrupt flag */
   SREG = sreg;
}

 

раз пишут, что надо заворачивать, значит надо заворачивать )))

bwn
Offline
Зарегистрирован: 25.08.2014

ЕвгенийП пишет:

Может и имеет, но в принципе ... даташит надо внимательнее читать :)

Евгений Петрович, не сыпьте соль на рану, что поделать, если последний зачет по аглицкому, в далеком СССР, был сдан за красивый, старинный словарь из "Старой Книги", а когда понадобилось, тут уже, то дед Альцгеймер зайдет шашками подвигать, то бабушка Деменция, с пирожком, чайку попить.))))
Гугель, неправославный, иногда вроде помогает, но слабо.((((

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

Долго втыкал, чем в 16-битном таймере WGM mode 10 отличается от WGM mode 11. Так вот, если кому-то интересно, тестовый скетч (двухлучевой осциллограф поможет увидеть):

#include <util/atomic.h>

// Выбираем режим
// WGM #10 (PWM, Phase Correct, TOP = ICR1) - PWM на каналы A и B с одной и той же частотой
// или
// WGM #11 (PWM, Phase Correct, TOP = OCR1A) - PWM на один канал B
//#define WGM_MODE_10

// TOP таймера
const uint16_t timerTop = 255;

int8_t   pwmStep = 1;
uint16_t  pwmValue = 5;
const uint32_t shortPause = 25;
const uint32_t longPause = 5000;

void setup() {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  TCCR1A = TCCR1B = 0x00;
  OCR1B = OCR1A = 0x00;
#ifdef WGM_MODE_10
  // Режим канала A - неинвертированный
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);
  // Режим канала B - неинвертированный
  TCCR1A |= (1 << COM1B1) | (0 << COM1B0);
  // WGM режим #10
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM13);
  ICR1 = timerTop;
#else
  // Режим канала A - неподключен к пину OC1A
  TCCR1A |= (0 << COM1A1) | (0 << COM1A0);
  // Режим канала B - неинвертированный
  TCCR1A |= (1 << COM1B0) | (1 << COM1B1);
  // WGM режим #11
  TCCR1A |= (1 << WGM11) | (1 << WGM10);
  TCCR1B |= (1 << WGM13);
  OCR1A = timerTop;
#endif
  // Делитель частоты /64
  TCCR1B |= (1 << CS10) | (1 << CS11);

}

void loop() {
  uint8_t flipFlop = false;
  static uint8_t fireLed = false;

  // Изменение скважности
  pwmValue += pwmStep;
  if (timerTop == pwmValue || 0x00 == pwmValue) {
    pwmStep = -pwmStep;
    flipFlop = true;
    fireLed = !fireLed;
  }

#ifdef WGM_MODE_10
  // Вычисление новых значений скважности для каналов А и B
  uint16_t newOCR1A = pwmValue;
  uint16_t newOCR1B = timerTop - pwmValue;
  // Атомарная установка новых значений регистров
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    OCR1A = newOCR1A;
    OCR1B = newOCR1B;
  }
#else
  // Вычисление новых значений скважности для канала B
  uint16_t newOCR1B = pwmValue;
  // Атомарная установка нового значения регистра
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    OCR1B = newOCR1B;
  }
  // Канал A используем, как хотим.
  digitalWrite(9, fireLed);
#endif
  // При смене направления - пауза дольше
  delay(flipFlop ? longPause : shortPause);
}

А если кратко, то:
1) Применяя ICR1 в качестве TOP мы можем использовать два канала (A, B) для PWM с разной скважностью на произвольной частоте.
2) Применяя OCR1A в качестве TOP мы можем получить один канал (B) для PWM на произвольной частоте и один (A) - для других нужд. А так же использовать таймер захвата - ICR1-то не занят.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Закину картинки для наглядности:

 

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016
#define  ONE_KHZ 

//
//  Делители таймеров на 1 кГц 
//
#ifdef  ONE_KHZ //  на самом деле 980Гц
  constexpr byte prescaler0 = bit(CS00) | bit(CS01);
  constexpr byte prescaler1 = bit(CS10) | bit(CS11);
  constexpr byte prescaler2 = bit(CS22);
//
//  Делители таймеров на 8 кГц 
//
#else // 8кГц (на самом деле 7812.5 Гц)
  constexpr byte prescaler0 = bit(CS01);
  constexpr byte prescaler1 = bit(CS11);
  constexpr byte prescaler2 = bit(CS21);
#endif

//
//  Настройка таймера 0 на FastPWM
//
void setupTimer0(void) {
  TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
  TCCR0B = prescaler0;
  TCNT0 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR0A = 80; //  уставнока скважности
  OCR0B = 150;  //  уставнока скважности
  TIMSK0 = 0;
}

//
//  Настройка таймера 1 на FastPWM
//
void setupTimer1(void) {
  TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM10);
  TCCR1B =  bit(WGM12) | prescaler1;
  TCNT1 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR1A = 60; //  уставнока скважности
  OCR1B = 200;  //  уставнока скважности
  TIMSK1 = 0;
}

//
//  Настройка таймера 2 на FastPWM
//
void setupTimer2(void) {
  TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
  TCCR2B = prescaler2;
  TCNT2 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR2A = 85; //  уставнока скважности
  OCR2B = 170;  //  уставнока скважности
  TIMSK2 = 0;
}

unsigned long mill;
unsigned int chas = 0;
unsigned int mins = 0;
unsigned int seks = 0;

void setup(void) {
  Serial.begin(115200);
  Serial.println("Fun is here");
  Serial.println(" ");
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  //
  //  Обесточить ВСЕ таймеры
  //
  PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  //
  setupTimer0();
  setupTimer1();
  setupTimer2();
  //
  //  Запустить ВСЕ таймеры одной командой процессора
  //
  PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  
  mill = millis(); // Запустим часики
}


void loop(void){
  if(millis()-mill >=1000){
    seks++;
    mill=mill+1000; //Именно так, нам нужна долговременная точность часиков для проверки
     Serial.print(chas);
      Serial.print(":");
        Serial.print(mins);
         Serial.print(":");
          Serial.println(seks);
  }
  if(seks==60){
    mins++;
    seks=0;
  }
  if(mins==60){
    chas++;
    mins=0;
  }
 
  }

и шимим

#define  ONE_KHZ 

//
//  Делители таймеров на 1 кГц 
//
#ifdef  ONE_KHZ //  на самом деле 980Гц
  constexpr byte prescaler0 = bit(CS00) | bit(CS01);
  constexpr byte prescaler1 = bit(CS10) | bit(CS11);
  constexpr byte prescaler2 = bit(CS22);
//
//  Делители таймеров на 8 кГц 
//
#else // 8кГц (на самом деле 7812.5 Гц)
  constexpr byte prescaler0 = bit(CS01);
  constexpr byte prescaler1 = bit(CS11);
  constexpr byte prescaler2 = bit(CS21);
#endif

//
//  Настройка таймера 0 на FastPWM
//
void setupTimer0(void) {
  TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
  TCCR0B = prescaler0;
  TCNT0 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR0A = 80; //  уставнока скважности
  OCR0B = 150;  //  уставнока скважности
  TIMSK0 = 0;
}

//
//  Настройка таймера 1 на FastPWM
//
void setupTimer1(void) {
  TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM10);
  TCCR1B =  bit(WGM12) | prescaler1;
  TCNT1 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR1A = 60; //  уставнока скважности
  OCR1B = 200;  //  уставнока скважности
  TIMSK1 = 0;
}

//
//  Настройка таймера 2 на FastPWM
//
void setupTimer2(void) {
  TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
  TCCR2B = prescaler2;
  TCNT2 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR2A = 85; //  уставнока скважности
  OCR2B = 170;  //  уставнока скважности
  TIMSK2 = 0;
}

unsigned long mill;
unsigned int chas = 0;
unsigned int mins = 0;
unsigned int seks = 0;
volatile byte pwm1 = 80;
volatile byte pwm2 = 150;
volatile byte pwm3 = 60;
volatile byte pwm4 = 200;
volatile byte pwm5 = 85;
volatile byte pwm6 = 170;

void setup(void) {
  Serial.begin(115200);
  Serial.println("Fun is here");
  Serial.println(" ");
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  //
  //  Обесточить ВСЕ таймеры
  //
  PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  //
  setupTimer0();
  setupTimer1();
  setupTimer2();
  //
  //  Запустить ВСЕ таймеры одной командой процессора
  //
  PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  
  mill = millis(); // Запустим часики
}


void loop(void){
  if(millis()-mill >=1000){
    seks++;
    mill=mill+1000; //Именно так, нам нужна долговременная точность часиков для проверки
   
  OCR0A = ++pwm1;  //  уставнока скважности таймер 0
  OCR0B = ++pwm2;  //  уставнока скважности
  OCR1A = ++pwm3;  //  уставнока скважности таймер 1
  OCR1B = ++pwm4;; //  уставнока скважности
  OCR2A = ++pwm5;  //  уставнока скважности таймер 2
  OCR2B = ++pwm6;  //  уставнока скважности

   if(seks==60){
    mins++;
    seks=0;
   }
  if(mins==60){
    chas++;
    mins=0;
   }
   if(chas==24){
    chas=0;
   }
   if(chas<10){
        Serial.print("0");
         }
      Serial.print(chas); 
      Serial.print(":");
   if(mins<10){
      Serial.print("0");
         }
      Serial.print(mins);
      Serial.print(":");
   if(seks<10){
      Serial.print("0");
         }
      Serial.println(seks);
  
    }
  }
    

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Вот пример на приблизительно 1 кГц и приблизительно 8 кГц (для 8 кГц закомментируйте строку №1)

#define	ONE_KHZ	

//
//	Делители таймеров на 1 кГц 
//
#ifdef	ONE_KHZ //	на самом деле 980Гц
	constexpr byte prescaler0 = bit(CS00) | bit(CS01);
	constexpr byte prescaler1 = bit(CS10) | bit(CS11);
	constexpr byte prescaler2 = bit(CS22);
//
//	Делители таймеров на 8 кГц 
//
#else	// 8кГц (на самом деле 7812.5 Гц)
	constexpr byte prescaler0 = bit(CS01);
	constexpr byte prescaler1 = bit(CS11);
	constexpr byte prescaler2 = bit(CS21);
#endif

//
//	Настройка таймера 0 на FastPWM
//
void setupTimer0(void) {
	TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
	TCCR0B = prescaler0;
	TCNT0 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR0A = 80;	//	уставнока скважности
	OCR0B = 150;	//	уставнока скважности
	TIMSK0 = 0;
}

//
//	Настройка таймера 1 на FastPWM
//
void setupTimer1(void) {
	TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM10);
	TCCR1B =  bit(WGM12) | prescaler1;
	TCNT1 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR1A = 60;	//	уставнока скважности
	OCR1B = 200;	//	уставнока скважности
	TIMSK1 = 0;
}

//
//	Настройка таймера 2 на FastPWM
//
void setupTimer2(void) {
	TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
	TCCR2B = prescaler2;
	TCNT2 = 0; // нужно, чтобы все таймеры стартовали синхронно
	OCR2A = 85;	//	уставнока скважности
	OCR2B = 170;	//	уставнока скважности
	TIMSK2 = 0;
}

void setup(void) {
	Serial.begin(57600);
	Serial.print("Fun is here");
	pinMode(3, OUTPUT);
	pinMode(11, OUTPUT);
	pinMode(9, OUTPUT);
	pinMode(10, OUTPUT);
	pinMode(5, OUTPUT);
	pinMode(6, OUTPUT);
	//
	//	Обесточить ВСЕ таймеры
	//
	PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
	//
	setupTimer0();
	setupTimer1();
	setupTimer2();
	//
	//	Запустить ВСЕ таймеры одной командой процессора
	//
	PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
}

void loop(void){}

а вот с регистром PRR всё не так однозначно оказалось (((

 

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

В смысле? Что неоднозначно?

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

Видимо он обесточил ближайшую подстанцию этим регистром...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

В смысле? Что неоднозначно?

да как-то исчезает ШИМ на пинах, подстанцию вроде не обесточивал )))

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

Ну, пример "от димакса" точно работает - я проверял говнокодом в лупе, прежде чем тиснуть в газету пост.

 

...
void loop() {
  delay(5000);
  cbi(TCCR1B, CS11);
  cbi(TCCR2B, CS21);
  delay(5000);
  GTCCR = (1 << TSM) | (1 << PSRASY) | (1 << PSRSYNC);
  TCNT1 = 0x00;
  TCNT2 = 0x00;
  sbi(TCCR1B, CS11);
  sbi(TCCR2B, CS21);
  GTCCR = 0;
  delay(5000);
  cbi(TCCR1B, CS11);
  cbi(TCCR2B, CS21);
  delay(5000);
  TCNT1 = 0x00;
  TCNT2 = 0x00;
  sbi(TCCR1B, CS11);
  sbi(TCCR2B, CS21);
  delay(5000);
}
...

 

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

Да оба примера нормально работают, я оба проверял.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

у меня тоже, я о PRR...

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Да оба примера нормально работают, я оба проверял.

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


#define PIN_ZUM 13
#define  ONE_KHZ 

//
//  Делители таймеров на 1 кГц 
//
#ifdef  ONE_KHZ //  на самом деле 980Гц
  constexpr byte prescaler0 = bit(CS00) | bit(CS01);
  constexpr byte prescaler1 = bit(CS10) | bit(CS11);
  constexpr byte prescaler2 = bit(CS22);
//
//  Делители таймеров на 8 кГц 
//
#else // 8кГц (на самом деле 7812.5 Гц)
  constexpr byte prescaler0 = bit(CS01);
  constexpr byte prescaler1 = bit(CS11);
  constexpr byte prescaler2 = bit(CS21);
#endif

//
//  Настройка таймера 0 на FastPWM
//
void setupTimer0(void) {
  TCCR0A = bit(COM0A1) | bit(COM0B1) | bit(WGM01) | bit(WGM00);
  TCCR0B = prescaler0;
  TCNT0 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR0A = 80; //  уставнока скважности
  OCR0B = 150;  //  уставнока скважности
  TIMSK0 = 0;
}

//
//  Настройка таймера 1 на FastPWM
//
void setupTimer1(void) {
  TCCR1A = bit(COM1A1) | bit(COM1B1) | bit(WGM10);
  TCCR1B =  bit(WGM12) | prescaler1;
  TCNT1 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR1A = 60; //  уставнока скважности
  OCR1B = 200;  //  уставнока скважности
  TIMSK1 = 0;
}

//
//  Настройка таймера 2 на FastPWM
//
void setupTimer2(void) {
  TCCR2A = bit(COM2A1) | bit(COM2B1) | bit(WGM21) | bit(WGM20);
  TCCR2B = prescaler2;
  TCNT2 = 0; // нужно, чтобы все таймеры стартовали синхронно
  OCR2A = 85; //  уставнока скважности
  OCR2B = 170;  //  уставнока скважности
  TIMSK2 = 0;
}

void setup(void) {
  Serial.begin(57600);
  Serial.print("Fun is here");
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  //
  //  Обесточить ВСЕ таймеры
  //
  PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  //
  setupTimer0();
  setupTimer1();
  setupTimer2();
  //
  //  Запустить ВСЕ таймеры одной командой процессора
  //
  PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));

  start_Buzzer();
}

/*******************ПИЩАЛКА ********************/
void start_Buzzer(){
     pinMode(PIN_ZUM,OUTPUT);
     attachInterrupt(1, Buzzer, RISING );
 //    analogWrite(pinINT1,0x80); //установим на пине частоту 
                                //490 гц скважность 2
 }
/* *
void stop_Buzzer(){
     detachInterrupt(1);
     digitalWrite(PIN_ZUM,LOW);
 }
/* */

void Buzzer(void){
     static int i=1000;
     if(!i--)
     {
    digitalWrite(PIN_ZUM, ! digitalRead(PIN_ZUM));
    i=1000;
      }
} 


void loop(void){}

 

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

У меня мигает на полгерца примерно

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

У меня мигает на полгерца примерно

 а Ардуино какая? испытываю на нано

я жеж говорю, точнее DIMAX, если у ua6em что-то имеет теоретическую возможность не заработать, оно обязательно не заработает )))

я сначала грешным делом подумал, что мне осциллограф спалили, но тут что-то другое, разберусь, отпишу...

другой компьютер, другая нано (рободин), загрузчик не перешивался, если не запускать синхронно (закомментировать строки 68 и 76) шим есть, светодиод моргает раз в секунду где-то, снять комментарии - после заливки разовая короткая вспышка и всё, шима нет... ЧЯДНТ...пните в нужном направлении, что посмотреть, была мысль, что проблема в загрузчике, здесь же он не менялся...

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

ЕвгенийП пишет:

В смысле?

Пепелац улетел.... А далее никаких "в смысле" не существует.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

"не могу понять" (c) Шейнин, записки следователя...
Если остановку таймеров выносить после их иницализации, то код работает, если до - нет

IDE 1.8.9 arduino nano ROBODYN

void setup(void) {
  Serial.begin(57600);
  Serial.print("Fun is here");
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  //
  //  Обесточить ВСЕ таймеры
  //
  //PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  //
  setupTimer0();
  //PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  setupTimer1();
  //PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));
  setupTimer2();
  //
  //  Запустить ВСЕ таймеры одной командой процессора
  //
  PRR |= (bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));

  PRR &= ~(bit(PRTIM0) | bit(PRTIM1) | bit(PRTIM2));

  start_Buzzer();
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Долго втыкал, чем в 16-битном таймере WGM mode 10 отличается от WGM mode 11. Так вот, если кому-то интересно, тестовый скетч (двухлучевой осциллограф поможет увидеть):

#include <util/atomic.h>

// Выбираем режим
// WGM #10 (PWM, Phase Correct, TOP = ICR1) - PWM на каналы A и B с одной и той же частотой
// или
// WGM #11 (PWM, Phase Correct, TOP = OCR1A) - PWM на один канал B
//#define WGM_MODE_10

// TOP таймера
const uint16_t timerTop = 255;

int8_t   pwmStep = 1;
uint16_t  pwmValue = 5;
const uint32_t shortPause = 25;
const uint32_t longPause = 5000;

void setup() {
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  TCCR1A = TCCR1B = 0x00;
  OCR1B = OCR1A = 0x00;
#ifdef WGM_MODE_10
  // Режим канала A - неинвертированный
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);
  // Режим канала B - неинвертированный
  TCCR1A |= (1 << COM1B1) | (0 << COM1B0);
  // WGM режим #10
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM13);
  ICR1 = timerTop;
#else
  // Режим канала A - неподключен к пину OC1A
  TCCR1A |= (0 << COM1A1) | (0 << COM1A0);
  // Режим канала B - неинвертированный
  TCCR1A |= (1 << COM1B0) | (1 << COM1B1);
  // WGM режим #11
  TCCR1A |= (1 << WGM11) | (1 << WGM10);
  TCCR1B |= (1 << WGM13);
  OCR1A = timerTop;
#endif
  // Делитель частоты /64
  TCCR1B |= (1 << CS10) | (1 << CS11);

}

void loop() {
  uint8_t flipFlop = false;
  static uint8_t fireLed = false;

  // Изменение скважности
  pwmValue += pwmStep;
  if (timerTop == pwmValue || 0x00 == pwmValue) {
    pwmStep = -pwmStep;
    flipFlop = true;
    fireLed = !fireLed;
  }

#ifdef WGM_MODE_10
  // Вычисление новых значений скважности для каналов А и B
  uint16_t newOCR1A = pwmValue;
  uint16_t newOCR1B = timerTop - pwmValue;
  // Атомарная установка новых значений регистров
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    OCR1A = newOCR1A;
    OCR1B = newOCR1B;
  }
#else
  // Вычисление новых значений скважности для канала B
  uint16_t newOCR1B = pwmValue;
  // Атомарная установка нового значения регистра
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    OCR1B = newOCR1B;
  }
  // Канал A используем, как хотим.
  digitalWrite(9, fireLed);
#endif
  // При смене направления - пауза дольше
  delay(flipFlop ? longPause : shortPause);
}

А если кратко, то:
1) Применяя ICR1 в качестве TOP мы можем использовать два канала (A, B) для PWM с разной скважностью на произвольной частоте.
2) Применяя OCR1A в качестве TOP мы можем получить один канал (B) для PWM на произвольной частоте и один (A) - для других нужд. А так же использовать таймер захвата - ICR1-то не занят.

кстати увидел у DIMAX (я о строке 43)
 

TCCR1B |= (3 << CS10) ;