Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Программирование таймеров Attiny45 и 84 - есть проблема
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
van1
Доброе время суток.

Есть такая проблема. Сделал ПИД-регулятор на ардуино Уно: светодиод на ШИМ плюс фоторезистор. Управление таймером. Все ок, работает. Решил перейти на Attiny45. Всё перенес, и вижу, что работать-то работает, но сигнал нестабилен. Путем проб выяснил, что контроллер выдает плохую (нестабильную) ШИМ. Написал свою ШИМ по известному алгоритму (таймер плюс прямое управлени портами). И все бы хорошо, да все равно ШИМ плохая. Предположил, что я как-то не так программирую таймер - не выдает он у меня 256-8000000/1/8000000 = 255 на Timer1, а выдает гораздо меньше.

Перешел на Attiny 84. А там еще хуже. Как ни бился, мне не удалось получить от таймеров (обоих) высокочастотное тактирование. И OVF и СOMPA пробовал. Вероятно, я что-то делаю не так.

Само собой, даташиты курил. имена регистров пишу без ошибок. Но, походу, глобально что-то забываю/не понимаю, т.к. опыт имею не шибко большой. Возможно, какие-то биты не пишу из нужных, возможно, вообще задаю непрвильный режим работы таймера. Повторюсь, основная проблема в ом, что таймеры тактирубтся на более низких частотах. чем мне надо, т.е. светодиодик мигает не достаточно быстро для полноценной ШИМ.

Может быть кто-то программировал таймеры на Attiny45/84 и поделится добрым советом или кусочком кода?

И второй вопрос: как еще можно получить качественную ШИМ, без мелких биений вокруг level (аналог функции analogWrite(pin, level))?

Пример для Attiny45 (OVF или COMPA на выбор): //Если можете ответить на вопросы выше без прочтения кода, то не забивайте им головуsm.gif

CODE
void initTimer0() {

noInterrupts();
DDRB = (1 << PB4);
TCCR0A = 0;
TCCR0B = 0;

timer0_counter = 255;//256-(int)(8000000/8/1000000); //Должен мигать на 1000000 Гц
TCNT0 = timer0_counter; //OVF
// TCNT0 = 0; //COMPA
// OCR0A = timer0_counter; //COMPA

TCCR0B |= (1<<CS01); // 8 prescaler

// TCCR0B |= (1 << CS02);//256
//TCCR0B |= (1 << CS02) | (1 << CS00); //1024

TIMSK |= (1 << TOIE0); //OVF
// TIMSK |= (1<< OCIE0A); //COMPA
interrupts();
}

ISR(TIMER0_OVF_vect)
//ISR(TIMER0_COMPA_vect)
{
TCNT0 = timer0_counter; // OVF preload timer


//digitalWrite(4, !digitalRead(4));
}

void initTimer1() {

timer1_counter = 255; //256-(int)(8000000/1024/7812); // Должен мигать на 7812 Гц

noInterrupts(); // disable all interrupts

DDRB = (1 << PB4);

TCCR1 = 0;
TCNT1=0;
/// OCR1A=timer1_counter;
OCR1A=timer1_counter; //timer_counter
//TCCR1 |= (1 << CS13) | (1 << CS10); // 256
// TCCR1 |= (1 << CS12); //8
TCCR1 |= (1 << CS10); //1
// TCCR1 |= (1 << CS13) | (1 << CS11) | (1 << CS10); //1024
TIMSK |= (1 << OCIE1A);
// TIMSK |= (1 << OCIE1B);

interrupts();
}

ISR(TIMER1_COMPA_vect)

{
if (counter == 0) {
buf_lev_ch1 = lev_ch1;
PORTB |=(1<<PB4);
}
if (counter == buf_lev_ch1) PORTB&=~(1<<PB4);

counter++;

if (counter == 10) counter = 0;
}

Для Attiny84:

CODE
void initTimer1() {

//sreg = SREG;

noInterrupts();

DDRB = (1 << PB2);


TCCR0A = 0;
TCCR0B = 0;

// TCCR1B |= (1<<WGM12);//COMPA

timer1_counter = 65536-(int)(8000000/1/8000000);
TCNT1 = timer1_counter; //OVF

// TCNT1 = 0; //COMPA
// OCR1A = timer1_counter; //COMPA

TCCR1B |= (1 << CS02) ; //256

// TCCR1B |= (1<<CS10); // 1 prescaler

TIMSK1 |= (1 << TOIE0); //OVF
// TIMSK1 |= (1<< OCIE1A); //COMPA
//SREG = sreg;
interrupts();

}

ISR(TIM1_OVF_vect)
//ISR(TIM1_COMPA_vect)
{
TCNT1 = timer1_counter; // OVF preload timer

digitalWrite(2, !digitalRead(2));
}
RabidRabbit
Всё же Вы не дочитали даташит. Например (timer 0, tiny45), устанавливаете режим Phase Correct PWM Mode (WGM[2:0] = 1), настраиваете поведение нужного Вам вывода (COM0A[1:0] или COM0B[1:0]), задаёте заполнение в соответствующем регистре OCR0A или OCR0B и включаете предделитель (CS0[2:0] = 1). Частота ШИМ будет = 8000000 / 510. И для "ручного" переключения состояния вывода достаточно записать 1 в соответствующий бит регистра PIN, а не заморачиваться с проверками, чтением и прочей фигнёй sm.gif
van1
Цитата(RabidRabbit @ Jun 4 2014, 07:30) *


А тогда вопрос: почему analogWrite для Attiny шумит, а для UNO гораздо меньше? Не в тактовой частоте ли дело?
van1
Цитата(RabidRabbit @ Jun 4 2014, 07:30) *
Всё же Вы не дочитали даташит. Например (timer 0, tiny45), устанавливаете режим Phase Correct PWM Mode (WGM[2:0] = 1), настраиваете поведение нужного Вам вывода (COM0A[1:0] или COM0B[1:0]), задаёте заполнение в соответствующем регистре OCR0A или OCR0B и включаете предделитель (CS0[2:0] = 1). Частота ШИМ будет = 8000000 / 510. И для "ручного" переключения состояния вывода достаточно записать 1 в соответствующий бит регистра PIN, а не заморачиваться с проверками, чтением и прочей фигнёй sm.gif



Кстати, спасибо, ШИМ действительно ровная получается. но это, к сожалению, все равно не ответ на вопрос, почему программная ШИМ не ровная. пока грешу на тактовую частоту.
gena_p1
Значит какие-то прерывания перебивают прерывания по таймеру. У вас два вектора прерываний по таймерам (пробежался по исходникам 45й). Выберите тот, что важнее, и запрещайте прерывания в обработчике, потом разрешайте. И неизвестно еще что компилятор на компилировал, может есть какой вектор на изменения состояния вывода, а по воздуху летают наводки.
Сергей Борщ
Цитата(gena_p1 @ Jun 19 2014, 11:51) *
Выберите тот, что важнее, и запрещайте прерывания в обработчике, потом разрешайте.
Нет, это не поможет. В AVR при входе в обработчик прерывания запрещаются автоматически. AVR не имеет вложенных прерываний, поэтому если прерывание ШИМ возникнет во время обработки другого прерывания - оно будет задержано до выхода из первого обработчика. Хочется получать одно прерывание без задержек - другие прерывания не должны использоваться вообще. Можно организовать некое подобие вложенных прерываний, но это не поможет избавиться от дрожания полностью, лишь частично его уменьшит.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.