Страница 1 из 1

Подскажите по ШИМ

Добавлено: Пт ноя 04, 2011 20:21:56
ipf
Подскажите как плавно включить а через 10 секунд плавно выключить все светодиоды на порту PORTD.

Сейчас включаю поочередно

void led_on(unsigned int mask){
unsigned char on_time;
for (on_time = 1; on_time < 100; on_time++) {
PORTC &= ~mask; // выключаем светодиод
delay(100-on_time); // отсчитываем время в выключенном состоянии
PORTC |= mask; // включаем светодиод
delay(on_time); // отсчитываем время во включенном состоянии
}


for (mask = 0x01; mask; mask <<= 1)
{
led_on(mask);
delay_ms(100);
}


как переделать под плавное включение всего порта сразу

Re: Подскажите по ШИМ

Добавлено: Сб ноя 05, 2011 20:35:53
Serhio
Извините, но решил написать здесь. Может есть у кого-нибудь код на асме для Мега8 ШИМ плавное зажигание светодиода? или подскажите пожалуйста алгоритм, чтобы я мог сравнить со своими додумками :)

Re: Подскажите по ШИМ

Добавлено: Сб ноя 05, 2011 20:41:24
GRAF
ururur писал(а):как переделать под плавное включение всего порта сразу
Включить все светодиоды к одной ноге с аппаратной ШИМ через транзистор и резисторы.

Re: Подскажите по ШИМ

Добавлено: Вс ноя 06, 2011 15:27:57
ipf
Serhio писал(а):Извините, но решил написать здесь. Может есть у кого-нибудь код на асме для Мега8 ШИМ плавное зажигание светодиода? или подскажите пожалуйста алгоритм, чтобы я мог сравнить со своими додумками :)
Могу дать на C. Код под мегу 8. Светодиод вешать на ножку PB1

Код: Выделить всё

#include <avr/io.h>//библиотека ввода/вывода
 
//Програма задержки
void wait (unsigned int a)
{ 
unsigned int i;
for (i=a;i>0;i--);
}
 
//Програма инициализации ШИМ
void pwm (void)
{
  TCCR1A=(1<<COM1A1)|(1<<WGM10); 
  TCCR1B=(1<<CS10);		 
  OCR1A=0x00;			//Начальная яркость нулевая
}
 
//Основная програма
int main(void)
{ 
unsigned char i;
 
 DDRB=0x02;   			//Делаем PB1  на выход
 pwm();
 
 while (1)
  { 
    for (i=0;i<255;i++)		//Плавно повышаем яркость 
	 {
	  OCR1A++;
	  wait(1000);                       // Ждем
	 }
    for (i=0;i<255;i++)		//Плавно понижаем яркость
	 {
	  OCR1A--;
	  wait(1000);                     Ждем
	 }
  }
 
 return 1;
}

Re: Подскажите по ШИМ

Добавлено: Вс ноя 06, 2011 16:02:54
Serhio
Благодарю :)

Подскажите пожалуйста как расчитать время задержки для ШИМ. Есть с обучалки задержка
Delay: out PortB,Temp

ldi Temp1,1
ldi Temp2,1
ldi Temp3,2

Loop: dec Temp1
brne Loop

dec Temp2
brne Loop

dec Temp3
brne Loop

ret

как узнать сколько миллисекунд она длится?

Re: Подскажите по ШИМ

Добавлено: Пн апр 23, 2012 20:52:39
EmDmAl
Здравствуйте. Написал примитивный код без прерываний для мигания 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 бит?

Код: Выделить всё

#include<avr/io.h>
#include<util/delay.h>
int main(void) {
 DDRD=(1<<PD4)|(1<<PD5);//PORTD ненадо настраивать
 TCCR1A=(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)|(0<<WGM11)|(1<<WGM10);
 TCCR1B=(0<<WGM13)|(1<<WGM12)|(0<<CS12)|(0<<CS11)|(1<<CS10);
 OCR1A = 0;
 OCR1B = 255;
 asm("cli");//Запрещаем прерывания
  while(1) {
   OCR1A++;
   OCR1B--;
   _delay_ms(10);
   if(OCR1A==255) {
    _delay_ms(100);
	OCR1A = 0;
	OCR1B = 255;
    _delay_ms(100);
   }
  }
}
Затем решил перевести на прерывания, но пока без задержек. Коэф-нт деления поставил 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мя вопросами. С уважением Дмитрий.

Re: Подскажите по ШИМ

Добавлено: Пн апр 23, 2012 21:04:48
Orion33
1. Для этих целей используют таймеры. Если задач немного, то можно для каждой использовать свой таймер. В последних Атмегах их 3. Если задач больше, надо реализовывать конвейер. Об этом хорошо написано на http://easyelectronics.ru/

3. Если не хватает встроенного счетчика, можно устроить свой, подсчитывая число срабатываний таймера. Для этого в обработчике прерывания инкрементируем переменную, а в теле программы проверяем ее значение. Если больше, запускаем обработчик и обнуляем. На инкремент тратится мало тактов, на проверку - тоже. Так что задержек не будет

Re: Подскажите по ШИМ

Добавлено: Пн апр 23, 2012 22:42:30
EmDmAl
Orion33 писал(а):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;
	}
  }
}

Re: Подскажите по ШИМ

Добавлено: Пн апр 23, 2012 23:14:20
EmDmAl
Решено: надо использовать предделитель 512, а в обработчике прерываний просто добавлять 'конечные автоматы' или промежуточные значения if(flza==0) {..} else { if(flza==1) {flza=2;} else {flza=0;}} Наглядней через switch.
Может кто знает как проще сделать напишите пожалуста.
Orion33 писал(а):Если задач больше, надо реализовывать конвейер. Об этом хорошо написано на http://easyelectronics.ru/
Я как раз там статьи и читал про ШИМ, но она на асме, и попробывал написать на Си. Только я не понял про какой 'конвейер' идет речь?

Re: Подскажите по ШИМ

Добавлено: Вт апр 24, 2012 09:06:44
Orion33
EmDmAl писал(а):
Orion33 писал(а):Если задач больше, надо реализовывать конвейер. Об этом хорошо написано на http://easyelectronics.ru/
Я как раз там статьи и читал про ШИМ, но она на асме, и попробывал написать на Си. Только я не понял про какой 'конвейер' идет речь?
Там про RTOS есть цикл статей, точно название сейчас не помню. Смысл в том, что единый таймер через определенные промежутки времени опрашивает очередь, выполняет операцию из начала и сдигает очередь. Другие прерывания могут вмешиваться в эту очередь и вставлять свои обработчики согласно приоритету. Сложно, но ИМХО правильное решение. Если один раз разобраться, можно потом просто задачи новые в конвейер добавлять, а ядро останется всегда одним)