Извините, но решил написать здесь. Может есть у кого-нибудь код на асме для Мега8 ШИМ плавное зажигание светодиода? или подскажите пожалуйста алгоритм, чтобы я мог сравнить со своими додумками
Извините, но решил написать здесь. Может есть у кого-нибудь код на асме для Мега8 ШИМ плавное зажигание светодиода? или подскажите пожалуйста алгоритм, чтобы я мог сравнить со своими додумками
Могу дать на C. Код под мегу 8. Светодиод вешать на ножку PB1
Код:
#include <avr/io.h>//библиотека ввода/вывода
//Програма задержки void wait (unsigned int a) { unsigned int i; for (i=a;i>0;i--); }
Здравствуйте. Написал примитивный код без прерываний для мигания 2х светодиодов, на таймере 1(aTMEGA32A).Свтодиод на канале А плавно гаснит, а на В зажигается. Предделитель поставил 1, период = 0,125мкс, но при задержки в 0,01с(_delay_ms(10)) период составляет 0,01*255+0,000125*255=2,55с. _delay_ms(100) - использую как-бы для "устаявшегося значения", но вопрос ни в этом. 1. Подскажите как реализовать задержку, не используя _delay_ms(10), потому как при этой функции микроконроллер просто отсчитывает время не делая больше ничего? 2. Таймер считает до 255 потому что мы взяли (0<<WGM13)|(1<<WGM12) = WGM1Х - Fast PWM 8 бит?
Затем решил перевести на прерывания, но пока без задержек. Коэф-нт деления поставил 1024, чтобы было видно затухание и зажигание соответственно. Написал код ниже:
Код:
#include<avr/io.h> #include <avr/interrupt.h> //Прерывание по совпадению канала А. Таймер 1 ISR (TIMER1_COMPA_vect) { PORTD|=(1<<PD5); OCR1A++; } //Прерывание по совпадению канала B. Таймер 1 ISR (TIMER1_COMPB_vect) { PORTD|=(1<<PD4); OCR1B--; } //Прерывание по переполнению Таймера 1 ISR (TIMER1_OVF_vect) { // flz=1; PORTD&=~(1<<PD5); PORTD&=~(1<<PD4); } int main(void) { //Главое все эти настройки регистров прописывать в главном теле DDRD=(1<<PD4)|(1<<PD5);//PORTD ненадо настраивать PORTD=(0<<PD4)|(0<<PD5); TCCR1A=(0<<WGM11)|(1<<WGM10); TCCR1B=(0<<WGM13)|(1<<WGM12)|(1<<CS12)|(0<<CS11)|(1<<CS10); //Вкл. прер. по совпадению в А, В и переполнению Т1 TIMSK=(1<<OCIE1A)|(1<<OCIE1B)|(1<<TOIE1); OCR1A=2; OCR1B=253; asm("sei");//Разрешаем все прерывания TCNT1=0; while(1) { if(OCR1A==255 || OCR1B==0) { OCR1A=0; OCR1B=255; } } } }
2. Но как сделать более плавное затухание и зажигание? Частоту снизить не получится(если только взять внешний кварц с меньшей частотой), т.к. делитель самый высокий 1024. Помогите разобраться с этими 3мя вопросами. С уважением Дмитрий.
1. Для этих целей используют таймеры. Если задач немного, то можно для каждой использовать свой таймер. В последних Атмегах их 3. Если задач больше, надо реализовывать конвейер. Об этом хорошо написано на http://easyelectronics.ru/
3. Если не хватает встроенного счетчика, можно устроить свой, подсчитывая число срабатываний таймера. Для этого в обработчике прерывания инкрементируем переменную, а в теле программы проверяем ее значение. Если больше, запускаем обработчик и обнуляем. На инкремент тратится мало тактов, на проверку - тоже. Так что задержек не будет
3. Если не хватает встроенного счетчика, можно устроить свой, подсчитывая число срабатываний таймера. Для этого в обработчике прерывания инкрементируем переменную, а в теле программы проверяем ее значение. Если больше, запускаем обработчик и обнуляем. На инкремент тратится мало тактов, на проверку - тоже. Так что задержек не будет
Пробывал добавить инкремент в обработчик прерываний, для снижения частоты(более долгом затухании и разгорании). Время затухания увелилось вдвое, но мерцание сильное. Надо подумать над плавностью смены сигнала. Хотя при делители 512 мерцания невидно.
Код:
#include<avr/io.h> #include <avr/interrupt.h> volatile char flza=0;//Флаг задержек А volatile char flzb=0;//Флаг задержек В
//Прерывание по совпадению канала А. Таймер 1 ISR (TIMER1_COMPA_vect) { if(flza==0) { flza=1; PORTD|=(1<<PD5); OCR1A++; } else {flza=0;} } //Прерывание по совпадению канала B. Таймер 1 ISR (TIMER1_COMPB_vect) { if(flzb==0) { flzb=1; PORTD|=(1<<PD4); OCR1B--; } else {flzb=0;} } //Прерывание по переполнению Таймера 1 ISR (TIMER1_OVF_vect) { PORTD&=~(1<<PD5); PORTD&=~(1<<PD4); } int main(void) { DDRD=(1<<PD4)|(1<<PD5);//PORTD ненадо настраивать PORTD=(0<<PD4)|(0<<PD5); TCCR1A=(0<<WGM11)|(1<<WGM10);//WGM1Х - Fast PWM 8 бит TCCR1B=(0<<WGM13)|(1<<WGM12)|(1<<CS12)|(0<<CS11)|(1<<CS10); TIMSK=(1<<OCIE1A)|(1<<OCIE1B)|(1<<TOIE1); OCR1A=2; OCR1B=253; asm("sei"); TCNT1=0; while(1) { if(OCR1A==255 || OCR1B==0) { OCR1A=0; OCR1B=255; } } }
Решено: надо использовать предделитель 512, а в обработчике прерываний просто добавлять 'конечные автоматы' или промежуточные значения if(flza==0) {..} else { if(flza==1) {flza=2;} else {flza=0;}} Наглядней через switch. Может кто знает как проще сделать напишите пожалуста.
Я как раз там статьи и читал про ШИМ, но она на асме, и попробывал написать на Си. Только я не понял про какой 'конвейер' идет речь?
Там про RTOS есть цикл статей, точно название сейчас не помню. Смысл в том, что единый таймер через определенные промежутки времени опрашивает очередь, выполняет операцию из начала и сдигает очередь. Другие прерывания могут вмешиваться в эту очередь и вставлять свои обработчики согласно приоритету. Сложно, но ИМХО правильное решение. Если один раз разобраться, можно потом просто задачи новые в конвейер добавлять, а ядро останется всегда одним)
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения