Управление RGB светодиодом

Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить
Держит паяльник хвостом
Аватара пользователя
Сообщения: 920
Зарегистрирован: Сб янв 16, 2010 15:34:57
Откуда: Sevsk/Moscow

Сообщение gauss »

Вот, посидел пару вечеров и написал управление для RGB. Код прилагается:

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

.def temp=R16 - под всякую фигню
.def ncur=R18 - номер текущей задачи
.def kf=R19 - коэффициент ШИС
.def sch=R20 - "счетчик задач"
.cseg
//Здесь таблица прерываний
Reset:
ldi temp,255
out DDRD,temp
ldi temp,0b00000101 //clk:128
out TCCR2,temp
ldi temp,0b11000000
out TIMSK,temp
ldi temp,0
out TCNT2,temp
ldi kf,1
out OCR2,kf
//тут иниц. стека
ldi sch,0
ldi ncur,0
rcall Load
ldi ncur,1
rcall Load
rcall CurTask
sei
Loop: rjmp Loop
TIM2_Comp:
rcall Load
rcall CurTask
reti
TIM2_OVF:
rcall Load
rcall CurTask
rcall Load
subi sch,3
rcall CurTask
reti
//Идет перечисление задач
Redon: SBI PortD,7	//0
ret
Greenon: SBI PortD,6	//1
ret
Blueon: SBI PortD,5			//2
ret
Redoff: CBI PortD,7		//3
ret
Greenoff: CBI PortD,6	//4
ret
Blueoff: CBI PortD,5	//5
ret
KU: 		//6 (kf - коэффициент Up - растет)
inc kf
out OCR2,kf
cpi kf,254
breq SbrosU
ret
SbrosU:
rcall CurTask
rcall Load
subi sch,-3
ret
KD: 		//7 (см. 6)
dec kf
out OCR2,kf
cpi kf,1
breq SbrosD
ret
SbrosD:
rcall CurTask
rcall Load
subi sch,-3
ret
Table: .dw RedOn,Greenon,BlueOn,Redoff,Greenoff,blueoff,KU,KD//0,1,2,3,4,5,6,7,8 - таблица "операций"
Tasks: .db 4,1,6,1,3,0,7,3,5,2,6,2,4,1,7,4,3,0,6,0,5,2,7,5	//задачи в порядке выполнения
Load:
cli
lsl ncur
ldi ZL,low(Table*2)
ldi ZH,high(Table*2)
ldi r17,0
ldi r16,0
add ZL,ncur
adc ZH,r17
lpm r16,Z+
lpm r17,Z
movw ZH:ZL,r17:r16
sei
icall	
ret
CurTask:	
cli
ldi ZL,low(Tasks*2)
ldi ZH,high(Tasks*2)
ldi r17,0
add ZL,sch
adc ZH,r17
lpm
mov ncur,R0
inc sch
sei
ret
Извиняюсь за громоздкий код :)
Вопрос такой:
1)Когда счетчик дозодит до конца массива, то что он загружает? Просто процедуры цикличного переливания нет, а программа возвращается к началу. Почему?
2)Вроде ШИМ везде одинаков. Но, например, переход от голубого к синему, а затем к фиолетовому - заметно падение яркости. При переходе, например, от красного к желтому и затем зеленого - такого заметного перехода нет. Это особенность человеческого зрения?
3)Как можно ещё оптимизировать программу?
Заранее благодарю за помощь!
:idea:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

gauss писал(а):1)Когда счетчик дозодит до конца массива, то что он загружает?
То, что идет дальше во флеше, а это коды команд (cli, lsl ncur, ldi ZL,low(Table*2)) и т.д)
gauss писал(а):[...]все остальные вопросы [...]
Сильно сомневаюсь что структура программы вообще верная...
После комента "Здесь таблица прерываний" самой таблицы нету, и куда при прерывании попадет контроллер неизвестно (скорее всего куда-то в район инициализации). Просто обозвать метку в произвольном месте "TIM2_Comp:" вовсе не обозначает что туда попадет обработчик прерывания (или она действительно по 23-му адресу???). Либо ставьте ее по нужному адресу, либо используйте директиву .org (но это тут вряд ли поможет).
А, и еще: "тут иниц. стека" - и где она??? :facepalm:
Короче исправляйте прогу, а там видно будет...
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 920
Зарегистрирован: Сб янв 16, 2010 15:34:57
Откуда: Sevsk/Moscow

Сообщение gauss »

1)То есть, получается, что фактически адрес массива загружается заного, поэтому все и работает?
На самом деле программа работает. Таблицу прерываний и иниц. стека не писал здесь, чтобы уменьшить объем кода.
Таблица выглядит так (взята из даташита):

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

.cseg
rjmp Reset
rjmp EXT_INT0 ; IRQ0 Handler
rjmp EXT_INT1 ; IRQ1 Handler
rjmp TIM2_COMP ; Timer2 Compare Handler
rjmp TIM2_OVF ; Timer2 Overflow Handler
rjmp TIM1_CAPT ; Timer1 Capture Handler
rjmp TIM1_COMPA ; Timer1 CompareA Handler
rjmp TIM1_COMPB ; Timer1 CompareB Handler
rjmp TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
rjmp SPI_STC ; SPI Transfer Complete Handler
rjmp USART_RXC ; USART RX Complete Handler
rjmp USART_UDRE ; UDR Empty Handler
rjmp USART_TXC ; USART TX Complete Handler
rjmp ADCC ; ADC Conversion Complete Handler
rjmp EE_RDY ; EEPROM Ready Handler
rjmp ANA_COMP ; Analog Comparator Handler
rjmp TWSI ; Two-wire Serial Interface
rjmp SPM_RDY
EXT_INT0:
EXT_INT1:
;TIM2_COMP:
;TIM2_OVF:
TIM1_CAPT:
TIM1_CompA:
TIM1_CompB:
TIM1_OVF:
TIM0_OVF:
SPI_STC:
USART_RXC:
USART_UDRE:
USART_TXC:
ADCC:
EE_RDY:
ANA_COMP:
TWSI:
SPM_RDY:		
reti	
Reset:
дальше остальная программа
Так что все прерывания прописаны верно :)
Иниц. стека просто не прописана -в коде программы она, естественно, есть:

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

ldi temp,high(Ramend)
out SPH,temp
ldi temp,low(RamEnd)
out SPL,temp
:idea:
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Вот блин, код не приводят целиком и не говорят что он сокращен, а мы тут гадаем :facepalm:
Нет, по новой ничего не загружается. После того, как

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

ldi ZL,low(Table*2)
ldi ZH,high(Table*2)
ldi r17,0
ldi r16,0
add ZL,ncur
adc ZH,r17
lpm r16,Z+
прочитает KD, следующим считанным числом будет 4, потом 1, т.е. то, что дальше идет, а кусок

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

ldi ZL,low(Tasks*2)
ldi ZH,high(Tasks*2)
ldi r17,0
add ZL,sch
adc ZH,r17
lpm
после 2,7,5 пойдет читать дальше флеш, т.е. код команды CLI, код команды LSL, потом коды LDI и констант table*2 и так пока ncur и sch не обнулятся (переполнятся), потом пойдет снова.
Реклама
Эиком - электронные компоненты и радиодетали
Держит паяльник хвостом
Аватара пользователя
Сообщения: 920
Зарегистрирован: Сб янв 16, 2010 15:34:57
Откуда: Sevsk/Moscow

Сообщение gauss »

gauss писал(а):Таблицу прерываний и иниц. стека не писал здесь, чтобы уменьшить объем кода.
Написал сразу :)
То есть лучше будет после массива Tasks прописать jmp Reset, чтобы начать программу сначала?
:idea:
Реклама
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1163
Зарегистрирован: Ср янв 05, 2011 16:25:15

Сообщение ChipKiller »

Engineer_Keen писал(а):Вот блин, код не приводят целиком и не говорят что он сокращен, а мы тут гадаем
... ну да - сначала нужно угадать, что хотел сделать ТС ... а это как правило сложнее всего :))
gauss - комментарии никто не отменял
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 920
Зарегистрирован: Сб янв 16, 2010 15:34:57
Откуда: Sevsk/Moscow

Сообщение gauss »

Хотел сделать плавное управление RGB светодиодом. Вот код с комментами:

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

//называется так,потому что первая программа, 
//в которой я использовал индексные переходы
//они сделаны для управления RGB сыетодиодом, 
//также для этого реализован ШИМ :)
//ТО, используется многозадачность и служба таймеров => прообраз ОС
//вторая версия кода
.def temp=R16
.def ncur=R18
.def kf=R19
.def sch=R20
.cseg
//ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ПРЕРЫВАНИЙ - НАЧАЛО
rjmp Reset
rjmp EXT_INT0 ; IRQ0 Handler
rjmp EXT_INT1 ; IRQ1 Handler
rjmp TIM2_COMP ; Timer2 Compare Handler
rjmp TIM2_OVF ; Timer2 Overflow Handler
rjmp TIM1_CAPT ; Timer1 Capture Handler
rjmp TIM1_COMPA ; Timer1 CompareA Handler
rjmp TIM1_COMPB ; Timer1 CompareB Handler
rjmp TIM1_OVF ; Timer1 Overflow Handler
rjmp TIM0_OVF ; Timer0 Overflow Handler
rjmp SPI_STC ; SPI Transfer Complete Handler
rjmp USART_RXC ; USART RX Complete Handler
rjmp USART_UDRE ; UDR Empty Handler
rjmp USART_TXC ; USART TX Complete Handler
rjmp ADCC ; ADC Conversion Complete Handler
rjmp EE_RDY ; EEPROM Ready Handler
rjmp ANA_COMP ; Analog Comparator Handler
rjmp TWSI ; Two-wire Serial Interface
rjmp SPM_RDY
EXT_INT0:
EXT_INT1:
;TIM2_COMP:
;TIM2_OVF:
TIM1_CAPT:
TIM1_CompA:
TIM1_CompB:
TIM1_OVF:
TIM0_OVF:
SPI_STC:
USART_RXC:
USART_UDRE:
USART_TXC:
ADCC:
EE_RDY:
ANA_COMP:
TWSI:
SPM_RDY:		
reti	//ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ПРЕРЫВАНИЙ - КОНЕЦ
Reset:
ldi temp,255
out DDRD,temp
ldi temp,0b00000101 //CLK:128
out TCCR2,temp
ldi temp,0b11000000	
out TIMSK,temp
ldi temp,0
out TCNT2,temp
ldi kf,1
out OCR2,kf
ldi temp,high(Ramend)
out SPH,temp
ldi temp,low(RamEnd)
out SPL,temp
ldi sch,0
ldi ncur,0
rcall Load
ldi ncur,1
rcall Load
rcall CurTask
sei
Loop: rjmp Loop
TIM2_Comp:
rcall Load
rcall CurTask
reti
TIM2_OVF:
rcall Load
rcall CurTask
rcall Load
subi sch,3
rcall CurTask
reti
Redon: SBI PortD,7	//0
ret
Greenon: SBI PortD,6	//1
ret
Blueon: SBI PortD,5			//2
ret
Redoff: CBI PortD,7		//3
ret
Greenoff: CBI PortD,6	//4
ret
Blueoff: CBI PortD,5	//5
//Задачи 0-5 отвечают за вкл/выкл светодиодов.
ret
KU: 		//6.Задача отвечает за повышение к-та ШИМ. При повышении до максимума загружает следующие задачи (другое переключение цвета)
inc kf
out OCR2,kf
cpi kf,254
breq SbrosU
ret
SbrosU:
rcall CurTask
rcall Load
subi sch,-3	//сделано для загрузки сделующей задачи
ret
KD: 		//7.Задача отвечает за снижение к-та ШИМ. При понижении до минимума загружает следующие задачи (другое переключение цвета)
dec kf
out OCR2,kf
cpi kf,1
breq SbrosD
ret
SbrosD:
rcall CurTask
rcall Load
subi sch,-3 //сделано для загрузки сделующей задачи
ret
Table: .dw RedOn,Greenon,BlueOn,Redoff,Greenoff,blueoff,KU,KD//0,1,2,3,4,5,6,7 - таблица "операций". Это таблица существующих задач.
Tasks: .db 4,1,6,1,3,0,7,3,5,2,6,2,4,1,7,4,3,0,6,0,5,2,7,5	//Массив, в котором находятся номера задач в порядке выполнения.
jmp Reset
Load:	//Функция выполняет задачу, номер которой загружен из массива
cli
lsl ncur
ldi ZL,low(Table*2)
ldi ZH,high(Table*2)
ldi r17,0
ldi r16,0
add ZL,ncur
adc ZH,r17
lpm r16,Z+	
lpm r17,Z
movw ZH:ZL,r17:r16
sei
icall	//переход по адресу, указанного в регистровой паре Z
ret
CurTask:	//Функция загружает в память номер задачи, и последующим инкрементом адреса (переменная sch)
cli
ldi ZL,low(Tasks*2)
ldi ZH,high(Tasks*2)
ldi r17,0
add ZL,sch
adc ZH,r17
lpm
mov ncur,R0
inc sch
sei
ret
:idea:
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

А на Си не интересно?
Намного короче получается.....

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

#define F_CPU 9600000
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t blue=0,red=42,green=42+64,counter,dir;
int main (void)
{
    DDRB = 0xff;//линии порта на вывод
    TCCR0A = 0x02;//режим таймера - CTC
    TCCR0B = 0x02;//предделителя таймера - деление на 8
    TIMSK0 = 0x04;//счетчика по совпадению с OCR0A
    OCR0A = 120;//регистр сравнения = 120 (прерывание 10khz)
    sei ();                                        
    while(1){
        _delay_ms (10);//скорость изминения
        if (blue++ & 0x40)     blue -= 2;
        if (blue == 0x40)     blue = 0;
        if (blue == 0x3f)     blue = 0x7f;
        if (green++ & 0x40) green -= 2;
        if (green == 0x40)     green = 0;
        if (green == 0x3f)     green = 0x7f;
        if (red++ & 0x40)     red -= 2;
        if (red == 0x40)     red = 0;
        if (red == 0x3f)     red = 0x7f;
    }
}
ISR (TIM0_COMPA_vect)
{    
    if (++counter == 64)    counter = 0;
    if ((blue & 0x3f)>=counter)    PORTB |= (1<<0);
    else PORTB &= ~(1<<0);
    if ((red & 0x3f)>=counter)    PORTB |= (1<<1);
    else PORTB &= ~(1<<1);
    if ((green & 0x3f)>=counter) PORTB |= (1<<2);
    else PORTB &= ~(1<<2);
} 
Изображение
Вложения
sh1.gif
(39.87 КБ) 1004 скачивания
Держит паяльник хвостом
Аватара пользователя
Сообщения: 920
Зарегистрирован: Сб янв 16, 2010 15:34:57
Откуда: Sevsk/Moscow

Сообщение gauss »

Сомневаюсь, что по объему и скорости меньше
:idea:
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

Ну ясное дело что на асме короче по объему выйдет, но важен же результат и практичность, а не то что большая часть памяти пустует.....
Данный вариант Сишной реализации отжирает 440 байт (43% у тиньки 13-й).
Даже остается место для раздолья, я уже не говорю про более старшие камни.
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1163
Зарегистрирован: Ср янв 05, 2011 16:25:15

Сообщение ChipKiller »

shads писал(а):Ну ясное дело что на асме короче по объему выйдет.....
... это смотря как писать, приведенная ТС "простыня" явно не пример для подражания.
PS. Ваш код наверное будет занимать поменьше Flash_a если заменить

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

        if (blue++ & 0x40)     blue -= 2;
        if (blue == 0x40)     blue = 0;
        if (blue == 0x3f)     blue = 0x7f;
        if (green++ & 0x40) green -= 2;
        if (green == 0x40)     green = 0;
        if (green == 0x3f)     green = 0x7f;
        if (red++ & 0x40)     red -= 2;
        if (red == 0x40)     red = 0;
        if (red == 0x3f)     red = 0x7f;
на

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

            
.....
void NextPWM(uint8_t *color){
        uint8_t pwm; pwm=*color;
        if (pwm++ & 0x40)     pwm -= 2;
        if (pwm == 0x40)     pwm = 0;
        if (pwm == 0x3f)     pwm = 0x7f;
        *color=pwm;
}
.....
            NextPWM(&blue);   
            NextPWM(&green);
            NextPWM(&red);
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

gauss писал(а):То есть лучше будет после массива Tasks прописать jmp Reset, чтобы начать программу сначала?
Не получится так. LPM считает код команды JMP в регистр (по 1 байту из 2), потом код адреса метки reset (тоже по 1 байту). Выполнять он его с чего будет? Выполняется только тот код, который находится по адресу в PC (счетчик команд). Вот если после LPM сделать сравнение регистра Z например с меткой в следующей после tasks строчке, то можно и jmp на сброс поставить, а можно просто обнулить счетчики которые к Z прибавляются.
Ответить

Вернуться в «Периферия»