van1 0 3 июня, 2014 Опубликовано 3 июня, 2014 (изменено) · Жалоба Доброе время суток. Есть такая проблема. Сделал ПИД-регулятор на ардуино Уно: светодиод на ШИМ плюс фоторезистор. Управление таймером. Все ок, работает. Решил перейти на Attiny45. Всё перенес, и вижу, что работать-то работает, но сигнал нестабилен. Путем проб выяснил, что контроллер выдает плохую (нестабильную) ШИМ. Написал свою ШИМ по известному алгоритму (таймер плюс прямое управлени портами). И все бы хорошо, да все равно ШИМ плохая. Предположил, что я как-то не так программирую таймер - не выдает он у меня 256-8000000/1/8000000 = 255 на Timer1, а выдает гораздо меньше. Перешел на Attiny 84. А там еще хуже. Как ни бился, мне не удалось получить от таймеров (обоих) высокочастотное тактирование. И OVF и СOMPA пробовал. Вероятно, я что-то делаю не так. Само собой, даташиты курил. имена регистров пишу без ошибок. Но, походу, глобально что-то забываю/не понимаю, т.к. опыт имею не шибко большой. Возможно, какие-то биты не пишу из нужных, возможно, вообще задаю непрвильный режим работы таймера. Повторюсь, основная проблема в ом, что таймеры тактирубтся на более низких частотах. чем мне надо, т.е. светодиодик мигает не достаточно быстро для полноценной ШИМ. Может быть кто-то программировал таймеры на Attiny45/84 и поделится добрым советом или кусочком кода? И второй вопрос: как еще можно получить качественную ШИМ, без мелких биений вокруг level (аналог функции analogWrite(pin, level))? Пример для Attiny45 (OVF или COMPA на выбор): //Если можете ответить на вопросы выше без прочтения кода, то не забивайте им голову:) 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: 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)); } Изменено 3 июня, 2014 пользователем IgorKossak [codebox] для длинного кода, [code] - для короткого!!! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RabidRabbit 0 4 июня, 2014 Опубликовано 4 июня, 2014 · Жалоба Всё же Вы не дочитали даташит. Например (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, а не заморачиваться с проверками, чтением и прочей фигнёй :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
van1 0 4 июня, 2014 Опубликовано 4 июня, 2014 · Жалоба А тогда вопрос: почему analogWrite для Attiny шумит, а для UNO гораздо меньше? Не в тактовой частоте ли дело? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
van1 0 4 июня, 2014 Опубликовано 4 июня, 2014 · Жалоба Всё же Вы не дочитали даташит. Например (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, а не заморачиваться с проверками, чтением и прочей фигнёй :) Кстати, спасибо, ШИМ действительно ровная получается. но это, к сожалению, все равно не ответ на вопрос, почему программная ШИМ не ровная. пока грешу на тактовую частоту. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
research1 0 19 июня, 2014 Опубликовано 19 июня, 2014 · Жалоба Значит какие-то прерывания перебивают прерывания по таймеру. У вас два вектора прерываний по таймерам (пробежался по исходникам 45й). Выберите тот, что важнее, и запрещайте прерывания в обработчике, потом разрешайте. И неизвестно еще что компилятор на компилировал, может есть какой вектор на изменения состояния вывода, а по воздуху летают наводки. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 121 19 июня, 2014 Опубликовано 19 июня, 2014 · Жалоба Выберите тот, что важнее, и запрещайте прерывания в обработчике, потом разрешайте.Нет, это не поможет. В AVR при входе в обработчик прерывания запрещаются автоматически. AVR не имеет вложенных прерываний, поэтому если прерывание ШИМ возникнет во время обработки другого прерывания - оно будет задержано до выхода из первого обработчика. Хочется получать одно прерывание без задержек - другие прерывания не должны использоваться вообще. Можно организовать некое подобие вложенных прерываний, но это не поможет избавиться от дрожания полностью, лишь частично его уменьшит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться