Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
lisok88
Родился
Сообщения: 4 Зарегистрирован: Пт май 18, 2012 23:03:42
Сообщение
lisok88 » Сб май 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);}
}
}
Еще вопрос. Как бы рациональнее все это осуществить. Просто через транзисторы или брать сразу драйвер для светодиодов. Заранее спасибо!!!
BOB51
Друг Кота
Сообщения: 15571 Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК
Сообщение
BOB51 » Сб май 19, 2012 17:31:53
на ассемблере подскажу, на Сии - избави...
Engineer_Keen
Друг Кота
Сообщения: 3872 Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва
Сообщение
Engineer_Keen » Пн май 21, 2012 08:33:52
Внутреннего кварца не бывает, бывает внутренний RC-генератор. Обычно у АВР-ок его можно выставить фьюзами до 8 МГц. У вас таймер переполняется с частотой 1МГц/256 = 3900Гц, а частота ШИМ получается 3900/164=24Гц, что конечно маловато. Можно не менять источник тактирования, вариантов много. Можно сделать прерывание по совпадению с регистром (OCRxx), занести в него 128 и в его обработчике скопировать обработчик из прерывания по переполнению - будет в 2 раза чаще. Можно перевести таймер в режим CTC, и занести в OCR например 32, тогда ШИМ будет с частотой в 8 раз больше.
lisok88
Родился
Сообщения: 4 Зарегистрирован: Пт май 18, 2012 23:03:42
Сообщение
lisok88 » Пн май 21, 2012 18:40:38
Спасибо!!! Я начинающий и пока не понимаю как это все сделать... Ладно надо тогда еще поучиться
)))
urry
Сверлит текстолит когтями
Сообщения: 1262 Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница
Контактная информация:
Сообщение
urry » Вт май 22, 2012 07:31:15
lisok88
Родился
Сообщения: 4 Зарегистрирован: Пт май 18, 2012 23:03:42
Сообщение
lisok88 » Сб июн 09, 2012 01:20:01
Поменял частоту на 8 Мгц мерцание пропало. Но заметил странную вещь. Программой предусматривается плавное изменение цвета. Сначала включается например красный и синий и меняется значение синего, потом синий и зеленый и т.д Но почему то не всегда происходит смешивание, т.е отдельно видно красный и отдельно синий. И еще интересно есть ли какая нибудь палитра для светодиодов, что бы быстро получить нужный тебе цвет? Или легче сделать схему для подборки вручную?
BOB51
Друг Кота
Сообщения: 15571 Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК
Сообщение
BOB51 » Сб июн 09, 2012 13:14:23
для глаза одно восприятие- для железа это совсем другое
один и тот же уровень по осциллографу вызовет разные ощущения на красном, зеленом или синем
да и индивидуальное восприятие во многом зависит от конструкции экрана
лучше предусмотреть возможность окончательной подгонки параметров на уже завершенной конструкции
BOB51
Друг Кота
Сообщения: 15571 Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК
Сообщение
BOB51 » Сб июн 09, 2012 19:33:10
ой-ой-ой!!!
на том bamе свои "камушки" имеются!!!
проверенно на практике - если работать с фиксированным уровнем все хорошо, но только попытайся сделать простенький приемчик - плавный перебор уровней от 0 до 0xFF и затем к 0 (по кольцу) сразу эта проблемка вылезет
lisok88
Родился
Сообщения: 4 Зарегистрирован: Пт май 18, 2012 23:03:42
Сообщение
lisok88 » Вт июн 12, 2012 13:26:35
Нашел инфу по BAMу и примеры реализации. Но код пока не очень понятен особенно какую роль играет здесь
direct
. Ладно попробую изучить что такое маски. Может кто разъяснит хоть немного? )) Вот код:
Код: Выделить всё
#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;
}
}
BOB51
Друг Кота
Сообщения: 15571 Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК
Сообщение
BOB51 » Вт июл 10, 2012 19:54:30
блин... опять Сии...
YS
Друг Кота
Сообщения: 7518 Зарегистрирован: Вс мар 29, 2009 22:09:05
Контактная информация:
Сообщение
YS » Вт июл 10, 2012 22:05:27
опять Сии...
Вообще, С гораздо читабельнее асма.
Топикстартер, а зачем именно программный ШИМ? Что мешает юзать аппаратный?
Разница между теорией и практикой на практике гораздо больше, чем в теории.
BOB51
Друг Кота
Сообщения: 15571 Зарегистрирован: Вт мар 16, 2010 22:02:27
Откуда: ДОНЕЦК
Сообщение
BOB51 » Ср июл 11, 2012 06:25:36
это как кто пишет и мыслит (и кто к чему привык) - можно и алгоритм нарисовать - а там уже не все ли равно на каком языке реализовать
кстати... неплохо бы наборчик алгоритмов собрать без привязки к конкретному семейству и языку...