+1 голос
Мне надо увеличить частоту ШИМ. У меня ШИМом управляется светодиодная лента. А для света лучше сделать частоту побольше.
(871 баллов) 3 32 74

3 Ответы

+3 голосов
 
Лучший ответ

Итак, таймер. Вроде вещь эта в AVR простая, но вызывает вопросы. Давайте разбираться вместе. 

В AVR все таймеры тактируются от того же источника тактовой частоты, что и ядро. Единственное, что его отделяет от этого источника - это делитель частоты. Важно понимать - делитель частоты ОДИН на все таймеры, но у него несколько выводов. На какой из этих выводов подключить выбранный таймер (любой таймер можно повесить на любой вывод) определяет программист. Давайте эти выводы и запишем:

#define Prescaler_1			Bit(CS10)					// Делитель тактов
#define Prescaler_8			Bit(CS11)
#define Prescaler_64		Bit(CS10) | Bit(CS11)
#define Prescaler_256		Bit(CS12)
#define Prescaler_1024		Bit(CS10) | Bit(CS12)

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

#define	Set_Prescaler1		Prescaler_1				// Установка предделителя для таймера 1

Я предполагаю, что МК тактируется от привычных 16МГц, поэтому делением основной тактовой частоты на 1, мы будем тактировать наш таймер частотой ээээ... 16 МГц. Если дать счетчику (а счетчик в таймере - это его главный функциональный элемент) считать до конца, т.е. до 2^16 = 65536, то мы получим, что счетчик будет переполняться примерно 244 раза в секунду. Наверное, это мало, поэтому этот процесс мы будем ускорять. Но прежде мы определим режим работы таймера. Их несколько. Я же выберу режим очистки счетчика таймера при достижении им заданного значения. Этот режим называется CTC:

#define Timer1_Mode_CTC		TCCR1B |= (1 << WGM12) | (1 << WGM13);

Чтобы наш таймер заставить считать, его достаточно подключить к делителю:

#define	Timer1_Start		TCCR1B |= Set_Prescaler1	// Пуск таймера1 

Теперь запишем функцию инициализации таймера:

inline void T1_Init(void)
{	
	TIMSK1 = Bit(TOIE1) | Bit(OCIE1B);		// Установка прерываний по переполнению и от компаратора 1B
	ICR1 = 10000;                  // Установка верхней границы счета
}

Здесь мы ограничиваем счет таймера значением 10000, т.е. при достижении этого значения, счетчик таймера автоматически сбросится на ноль. А также разрешаем уход на обработчики прерываний при переполнении счетчика (в нашем случае это 10 000) и при совпадении значений в счетчике и в компараторе 1B (можно использовать и 1A).

Ну и где-то в основном коде запишем:

	T1_Init();
    Timer1_Mode_CTC
	Timer1_Start;						// Включаем в работу таймер 1	

У 328 камня есть очень большой недостаток - выходы компараторов таймера жестко привязаны к выводам МК. У более новых тинек этот дефект исправлен, ну а мы этот дефект немного подправим программно. Одновременно, т.к. мы разрешили прерывания, то мы обязаны написать обработчики этих прерываний (хотя бы пустые). В противном случае сразу после возникновения прерывания МК будет перезагружен (это эффект от применения компилятора GCC, а не особенность МК!). Обработчики запишем так:

	//... Прерывание при переполнении таймера 1
ISR(TIMER1_OVF_vect)
{

}

	//... Прерывание от компаратора B таймера 1
ISR(TIMER1_COMPB_vect)
{

}

В обработчиках мы будем заниматься натуральным ногодрыгом. Мой выбор пал на ногу 1 порта A. Но выбрать можно любую другую свободную. Запишем это:

#define	PWM_PORT			PORTA			
#define	PWM_DDR			DDRA
#define PWM_PIN         PINA1   // но можно записать просто "1"

Т.к. сразу после загрузки МК у нас все порты настроены как входы, то где-то в основном коде проинициализируем нашу ножку как выход:

PWM_DDR |= (1<<PWM_PIN);

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

#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)          reg |= (1<<(bit))	

#define OFF_PWM_PIN()			ClearBit(PWM_PORT, PWM_PIN)
#define ON_PWM_PIN()			SetBit(PWM_PORT, PWM_PIN)

Ну и наши обработчики могут выглядеть примерно так:

	//... Прерывание при переполнении таймера 1
ISR(TIMER1_OVF_vect)
{
   OFF_PWM_PIN()
}

	//... Прерывание от компаратора B таймера 1
ISR(TIMER1_COMPB_vect)
{
   ON_PWM_PIN()
}

Скважность ШИМ сигнала меняется путем записи любого значения от 0 и до 10000 в регистр компаратора 1B:

OCR1B = 5000;

Ну вот и всё.

(1.2 тыс. баллов) 1 10 31
выбран
–2 голосов

Здесь приведен вариант изменения частоты ШИМ без использования библиотек

(208 баллов) 2 14
–3 голосов

Надо использовать аппаратные таймеры. Самый простой способ здесь.

(812 баллов) 1 5 24
Добро пожаловать на Бредборд! Сайт вопросов и ответов на тему Arduino, Raspberry Pi и хоббийной электроники в целом. Цель Бредборда — быть максимально полезным. Поэтому мы строго следим за соблюдением правил, боремся с холиворами и оффтопиком.
  1. BAR__MEN

    454 балл(ов)

  2. parovoZZ

    329 балл(ов)

  3. issaom

    217 балл(ов)

  4. Un_ka

    186 балл(ов)

  5. gokase

    127 балл(ов)

  6. dartWaiter

    76 балл(ов)

Награды месяца
1-е место: Arduino Nano 33 BLE Sense
2-е место: Arduino Nano 33 BLE
3-е место: Arduino Nano 33 IoT

...