Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Тема закрыта

Управление светодиодами RGB. Программный ШИМ AVR.

Сб май 19, 2012 13:09:16

Здравствуйте! Хотелось бы поуправлять светодиодами RGB, и не одним, а например десятью. Как я понимаю аппаратным ШИМ не получится, не хватит выводов . Попробовал программный, но светодиод заметно мерцает. Работает схема на внутреннем кварце частота 1 МГц. Может поэтому? Возможно ли частоту внутреннего кварца повысить или подключать внешний? Микросхема Atmega16. Вот программа:
Код:
#include <mega8.h>
#include <delay.h>

volatile char pwm_counter,pwm_r,pwm_g,pwm_b;

/*** прерывание по переполнению Таймера 0 ***/
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
   if (pwm_counter++ > 163)
   {
        PORTB = 0x00;
   pwm_counter = 0;
   }
   if (pwm_counter > pwm_r)
   PORTB.1 = 1;
   if (pwm_counter > pwm_g)
   PORTB.2 = 1;
   if (pwm_counter > pwm_b)
   PORTB.3 = 1;   
}

/*** красный цвет ***/
void red (unsigned int time)
   {char a;
        for (a = 0; a<165; a++)
      {
                 pwm_r = 164 - a; //увеличение
            pwm_g = 164;
                 pwm_b = 164;
            delay_ms(time);
      }     
        for (a = 0; a<165; a++)
      {
                 pwm_r = a; //уменьшение
       pwm_g = 164;
       pwm_b = 164;
       delay_ms(time);
      }
   }

/*** зеленый цвет ***/
void green (unsigned int time)
   {char a;
        for (a = 0; a<165; a++)
      {
                 pwm_r = 164;
       pwm_g = 164 - a;
                 pwm_b = 164;
       delay_ms(time);
      }     
       
      for (a = 0; a<165; a++)
      {
                 pwm_r = 164;
       pwm_g = a;
       pwm_b = 164;
       delay_ms(time);
      }

   }
/*** синий цвет ***/
void blue (unsigned int time)
   {char a;
        for (a = 0; a<165; a++)
      {
                 pwm_r = 164;
       pwm_g = 164;
                 pwm_b = 164 - a;
       delay_ms(time);
      }     
        for (a = 0; a<165; a++)
      {
                 pwm_r = 164;
       pwm_g = 164;
       pwm_b = a;
       delay_ms(time);
      }
   }
/*** белый цвет ***/
void white (unsigned int time)
   {char a;
        for (a = 0; a<165; a++)
      {
                 pwm_r = 164 - a;
       pwm_g = 164 - a;
                 pwm_b = 164 - a;
       delay_ms(time);
      }     
        for (a = 0; a<165; a++)
      {
                 pwm_r = a;
       pwm_g = a;
       pwm_b = a;
       delay_ms(time);
      }
   }
/*** переход цветов ***/
void rgb (unsigned int time)
   {char a;
       for (a = 0; a<165; a++)
      {
       pwm_r = a;
       pwm_b = 164 - a;
       delay_ms(time);
      }

      for (a = 0; a<165; a++)
      {
       pwm_b = a;
       pwm_g = 164 - a;
       delay_ms(time);
      }
      
      for (a = 0; a<165; a++)
      {
       pwm_g = a;
       pwm_r = 164 - a;
       delay_ms(time);
      }
   }

void main (void)
{
   DDRB = 0x0E; // PB3,2,1 - выходы
        PORTB = 0x00;
        TIMSK = 0x01; // разрешение прерывания
   TCCR0 = 0x01; // без предделителя
   
   #asm("sei"); // глобальное разрешение прерываний

        while(1)
   {    
        red(5);
        green(5);      
        blue(5);
   white(10);
   for(;;)
   {rgb(100);}
   }
}




Еще вопрос. Как бы рациональнее все это осуществить. Просто через транзисторы или брать сразу драйвер для светодиодов. Заранее спасибо!!!

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Сб май 19, 2012 17:31:53

на ассемблере подскажу, на Сии - избави... :beer:

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Пн май 21, 2012 08:33:52

Внутреннего кварца не бывает, бывает внутренний RC-генератор. Обычно у АВР-ок его можно выставить фьюзами до 8 МГц. У вас таймер переполняется с частотой 1МГц/256 = 3900Гц, а частота ШИМ получается 3900/164=24Гц, что конечно маловато. Можно не менять источник тактирования, вариантов много. Можно сделать прерывание по совпадению с регистром (OCRxx), занести в него 128 и в его обработчике скопировать обработчик из прерывания по переполнению - будет в 2 раза чаще. Можно перевести таймер в режим CTC, и занести в OCR например 32, тогда ШИМ будет с частотой в 8 раз больше.

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Пн май 21, 2012 18:40:38

Спасибо!!! Я начинающий и пока не понимаю как это все сделать... Ладно надо тогда еще поучиться :)) )))

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Вт май 22, 2012 07:31:15

я здесь целую оперу написал
viewtopic.php?f=20&t=68801&p=1280463#p1280463

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Сб июн 09, 2012 01:20:01

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

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Сб июн 09, 2012 13:14:23

для глаза одно восприятие- для железа это совсем другое
один и тот же уровень по осциллографу вызовет разные ощущения на красном, зеленом или синем
да и индивидуальное восприятие во многом зависит от конструкции экрана
лучше предусмотреть возможность окончательной подгонки параметров на уже завершенной конструкции :beer:

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Сб июн 09, 2012 13:53:58

Вот здесь почитайте о программном ШИМ - BAM: Альтернатива ШИМу
И рядом - управление RGB светодиодом - Mood Lamp – лампочка с характером

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Сб июн 09, 2012 19:33:10

ой-ой-ой!!!
на том bamе свои "камушки" имеются!!!
проверенно на практике - если работать с фиксированным уровнем все хорошо, но только попытайся сделать простенький приемчик - плавный перебор уровней от 0 до 0xFF и затем к 0 (по кольцу) сразу эта проблемка вылезет 8)

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Вт июн 12, 2012 13:26:35

Нашел инфу по BAMу и примеры реализации. Но код пока не очень понятен особенно какую роль играет здесь
direct[i]
. Ладно попробую изучить что такое маски. Может кто разъяснит хоть немного? )) Вот код:
Код:
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//Global variables here
volatile unsigned char leds[22], direct[22]; //leds - непосредственно значения BAM для светодиодов, direct - направление изменения яркостей для эффектов
unsigned char mask_b, mask_c, mask_d;
unsigned char stage;

char compAArray[8][2] = { {0x4B, 0}, {0x96, 0}, {0x2C, 0x01}, {0x58, 0x02}, {0xB0, 0x04},
                                                                                                                {0x60, 0x09}, {0xC0, 0x12}, {0x80, 0x25} };
#define COMPA_HI 1
#define COMPA_LO 0

ISR(TIMER1_COMPA_vect)
{       
        //Паузу делаем после текущего бита. Т.е. если текущий - 7, пауза максимальная
        OCR1AH = compAArray[stage][COMPA_HI];
        OCR1AL = compAArray[stage][COMPA_LO];
       
        PORTB = mask_b;
        PORTC = mask_c;
        PORTD = mask_d;

        TCNT1 = 0;      //Обнуляем таймер-счетчик, иначе он будет продолжать считать до 0xFFFF
       
        //Следующее значение stage
        if (stage > 0)
        {
                stage --;
        } else
        {
                stage = 7;
        }
    mask_b = 0;
    mask_c = 0;
    mask_d = 0;
               
        //Вычисляем маски для следующего вызова прерывания
/************************************************/
        if( (leds[0] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 0);
        }
/**************************************/               
        if( (leds[1] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 1);
        }

/************************************************/
        if( (leds[2] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 2);
        }
/**************************************/               
        if( (leds[3] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 7);
        }               
/************************************************/             
        if( (leds[4] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 5);
        }
/**************************************/               
        if( (leds[5] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 6);
        }
/************************************************/             
        if( (leds[6] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 5);
        }
/**************************************/               
        if( (leds[7] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 4);
        }
/************************************************/             
        if( (leds[8] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 3);
        }
/**************************************/               
        if( (leds[9] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 3);
        }
/************************************************/             
        if( (leds[10] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 4);
        }
/**************************************/               
        if( (leds[11] & (1 << stage)) != 0)
        {
                mask_d |= (1 << 7);
        }
/************************************************/             
        if( (leds[12] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 0);
        }
/**************************************/               
        if( (leds[13] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 2);
        }
/************************************************/             
        if( (leds[14] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 1);
        }
/**************************************/               
        if( (leds[15] & (1 << stage)) != 0)
        {
                mask_c |= (1 << 0);
        }
/************************************************/             
        if( (leds[16] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 5);
        }       
/**************************************/               
        if( (leds[17] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 4);
        }
/************************************************/             
        if( (leds[18] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 3);
        }
/**************************************/               
        if( (leds[19] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 6);
        }
/************************************************/             
        if( (leds[20] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 2);
        }
/**************************************/               
        if( (leds[21] & (1 << stage)) != 0)
        {
                mask_b |= (1 << 1);
        }




}





void TimerInit(void)
{

        // Timer/Counter 1 initialization
        // Clock source: System Clock
        // Clock value: 8000,000 kHz
        // Mode: CTC top=OCR1A
        // OC1A output: Discon.
        // OC1B output: Discon.
        // Noise Canceler: Off
        // Input Capture on Falling Edge
        // Timer 1 Overflow Interrupt: On
        // Input Capture Interrupt: Off
        // Compare A Match Interrupt: Off
        // Compare B Match Interrupt: Off
        TCCR1A = 0x00;
        TCCR1B = 0x09;
        TCNT1H = 0x00;
        TCNT1L = 0x00;
        ICR1H  = 0x00;
        ICR1L  = 0x00;
        OCR1AH = 0x00;
        OCR1AL = 0x10;
        OCR1BH = 0x00;
        OCR1BL = 0x00;
        TIMSK  = 0x10;
}




int main(void)
{
        DDRB = 0xFF;
        DDRC = 0xFF;
        DDRD = 0xFF;
        TimerInit();
        stage = 7;
        mask_b = 0x00;
        mask_c = 0x00;
        mask_d = 0x00;
       
        unsigned char i, j, f = 0, mode = 0;
        for(i = 0; i < 22; i++)
                {
                        leds[i] = 0;
                        direct[i] = 0;
                }
        asm("sei");

        while(1)
        {

                switch(mode)
                {
                        case 0:
                                for(i = 0; i < 22; i++)
                                {
                                        leds[i] = rand();
                                }
               
                                _delay_ms(500);

                                break;
                        case 1:  //плавно гасим все
                                for(j = 0; j< 255; j++)
                                {
                                        for(i = 0; i< 22; i++)
                                        {
                                                if (leds[i]>0) { f = 1; leds[i]--; }
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(10);
                                                f = 0;
                                        }
                                }
                                break;
                       
                        case 2:  //плавно зажигаем все
                                for(j = 0; j< 255; j++)
                                {
                                        for(i = 0; i< 22; i++)
                                        {
                                                if (leds[i]<255) { leds[i]++; f = 1;}
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(8);
                                                f = 0;
                                        }
                                }
                                break;

                        case 3:  //плавно зажигаем случайно выбранные
                                for(i = 0; i< 22; i++)
                                {
                                        direct[i] = rand() % 2; //остаток от деления на 2 - четное/нечетное (1/0)
                                }
                                for(j = 0; j< 255; j++)
                                {       
                                        for(i = 0; i< 22; i++)
                                        {
                                                if ( (leds[i]<255) && (direct[i]==1) ) { f = 1; leds[i]++; }
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(10);
                                                f = 0;
                                        }
                                }
                                break;
                        case 4:  //плавно гасим случайно выбранные
                                for(i = 0; i< 22; i++)
                                {
                                        direct[i] = rand() % 2; //остаток от деления на 2 - четное/нечетное (1/0)
                                }
                                for(j = 0; j< 255; j++)
                                {       
                                        for(i = 0; i< 22; i++)
                                        {
                                                if ( (leds[i]>0) && (direct[i]==1) ) { f = 1; leds[i]--; }
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(10);
                                                f = 0;
                                        }
                                }
                                break;
                        case 5:  //Плавно зажигаем, потом тушим по очереди, зажигаем по очереди
                               
                                for(j = 0; j< 255; j++) //Плавно зажигаем все
                                {
                                        for(i = 0; i< 22; i++)
                                        {
                                                if (leds[i]<255) { leds[i]++; f = 1; }
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(12);
                                                f = 0;
                                        }
                                }


                                for(i = 0; i< 22; i++)
                                {
                                        direct[i] = rand() % 2; //остаток от деления на 2 - четное/нечетное (1/0)
                                }
                                for(j = 0; j< 255; j++)
                                {       
                                        for(i = 0; i< 22; i++)
                                        {
                                                if ( (leds[i]>0) && (direct[i]==1) ) { f = 1; leds[i]--; }
                                        }
                                        if (f == 1)
                                        {
                                                _delay_ms(10);
                                                f = 0;
                                        }
                                }
                                break;
                }
                mode = rand() % 6;
        }

}

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Вт июл 10, 2012 19:54:30

блин... опять Сии... :cry:

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Вт июл 10, 2012 22:05:27

опять Сии...


Вообще, С гораздо читабельнее асма. :wink:

Топикстартер, а зачем именно программный ШИМ? Что мешает юзать аппаратный?

Re: Управление светодиодами RGB. Программный ШИМ AVR.

Ср июл 11, 2012 06:25:36

это как кто пишет и мыслит (и кто к чему привык) - можно и алгоритм нарисовать - а там уже не все ли равно на каком языке реализовать 8)
кстати... неплохо бы наборчик алгоритмов собрать без привязки к конкретному семейству и языку... :roll:
Тема закрыта