Страница 1 из 1
Управление адресной светодиодной лентой с помощью atmega8
Добавлено: Вт дек 19, 2023 21:55:10
Sharcer
Доброго времени дня! Помогите пожалуйста новичку. Пытаюсь управлять адресной светодиодной лентой (WS2811) с помощью atmega8 (далее - МК). Хочу просто зажечь самый первый светодиод в ленте зеленым цветом. Но у меня вся лента просто загорается белым.
Лента питается от отдельного источника 12В.
МК питается от 5В через USB. (тактирование от внутреннего кварца. Частоту в коде задал 8МГц)
Нули питания МК и ленты соединены между собой на макетной плате.
Управляющий сигнал идет от PORTB.1 через резистор 220 Ом.
В коде прописал 3 цикла (по одному на каждый цвет в светодиоде).
Насколько я знаю цвет загорается когда сперва передаем логическую единицу в течение 0.8мкс, а ноль в течение 0.45 мкс. А гаснет светодиод при передаче единицы в течение 0.4мкс, а нуля в течение 0.85мкс.
Зеленый цвет ставлю включаю (байты G7...G0), а красный(R7...R0) и синий(B7...B0) выключаю.
Может я в коде что то напутал? Или дело в МК и он не может такие управляющие импульсы выдавать? Нигде не могу найти решение. Везде только уроки по ардуино с уже готовыми библиотеками, в которых неизвестно что и как происходит. А я хочу сам ручками с нуля все сделать.
Сам код программы в Atmel Studio:
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
int main(void)
{
DDRB=0b1111111;
PORTB=0b0000000;
while (1)
{
for(int i=0;i<8;++i) //передаем биты 1 в G7...G0
{
PORTB=0b0000010;
_delay_us(0.

;
PORTB=0b0000000;
_delay_us(0.4);
}
for(int i=0;i<8;++i) //передаем биты 0 в R7...R0
{
PORTB=0b0000010;
_delay_us(0.4);
PORTB=0b0000000;
_delay_us(0.85);
}
for(int i=0;i<8;++i) //передаем биты 0 в B7...B0
{
PORTB=0b0000010;
_delay_us(0.4);
PORTB=0b0000000;
_delay_us(0.85);
}
_delay_us(50);
}
}
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Вт дек 19, 2023 23:13:43
pasha_zv
посмотри осцилом - что ты передаешь. будет три байтных посылок с зазорами. это не прокатит.
растаскивай свои байты втрое - в битовую последовательность и передавай по SPI.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 07:52:31
КРАМ
[uquote="Sharcer",url="/forum/viewtopic.php?p=4519494#p4519494"]сперва передаем логическую единицу в течение 0.8мкс, а ноль в течение 0.45 мкс. А гаснет светодиод при передаче единицы в течение 0.4мкс, а нуля в течение 0.85мкс.
for(int i=0;i<8;++i) //передаем биты 1 в G7...G0
{
PORTB=0b0000010;
_delay_us(0.8 );
PORTB=0b0000000;
_delay_us(0.4);
}
for(int i=0;i<8;++i) //передаем биты 0 в R7...R0
{
PORTB=0b0000010;
_delay_us(0.4);
PORTB=0b0000000;
_delay_us(0.85);
}[/uquote]
Это шуткатакой?
Любезный, у вас частота ядра МК - 8 МГц. То есть всего ОДНА одноцикловая инструкция (а они далеко не все одноцикловые, особенно условное и безусловное ветвление кода) выполняется за
0,125 мкс.
Каким таким таинственным способом может быть реализована функция
_delay_us(0.4), например, если она не кратна 0,125?
Каким таким образом вы полагаете исполнение цикла и управление портом за 0 мкс?
С чего вы вообще взяли, что функция
_delay_us() в состоянии оперировать дробным аргументом?
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 08:42:26
Ivanoff-iv
Я делал с ассемблерной вставкой
Спойлер
Код: Выделить всё
//void Send(uint8_t r, uint8_t g, uint8_t b) { // Послать элемент на ленту
//void Send(flash uint8_t *addr) { // Послать элемент на ленту по указателю из флеша
void Send(uint8_t *addr) { // Послать элемент на ленту по указателю из ОЗУ
#asm
;ldd r26,y+2 ; Загрузить в r26 &b
;ldd r27,y+1 ; Загрузить в r26 &r
;ld r25,y ; Загрузить в r26 g
MOVW R30,R26
;lpm r26,z++ ;Загрузка по указателю из флеш
;lpm r27,z++
;lpm r25,z
ld r26,z++ ;Загрузка по указателю из ОЗУ
ld r27,z++
ld r25,z
ldi r30,24 ; Загрузить в r22 количество передаваемых бит
dec r30 ; уменьшить количество передаваемых бит на 1
label_1: ; Начало цикла
brmi label_2 ; Если все биты передались выйти
in r31,0x18 ; запомним состояние порта !!!!0х18 - адрес порта, автоматизировать не смог!!!!
#endasm
PIN_WS2812=1; // поднимем пин
#asm
;настройка задержек при изменении тактовой частоты производится изменением числа НОПов
;nop ; Подождать
;nop ; Подождать
nop ; Подождать
sbrs r27,7 ; Пропустить следующую команду если бит в регистре r27 установлен
out 0x18,r31 ; восстановим состояние порта (низкий уровень на пине) - с этой командой не пляшет общее время посылки
; !!!!0х18 - адрес порта, автоматизировать не смог!!!!
;nop ; Подождать
nop ; Подождать
lsl r25 ; g<<1
rol r26 ; b<<1 через перенос чтобы была непрерывность
rol r27 ; r<<1 через перенос чтобы была непрерывность
dec r30 ; уменьшить количество передаваемых бит на 1
#endasm
PIN_WS2812=0; // Пин к земле
#asm
;nop ; Подождать
;nop ; Подождать
rjmp label_1 ; Перейти к началу цикла
label_2: ; Выйти из функции
#endasm
PIN_WS2812=0; // Пин к земле
}
код не допилен до удобоваримого вида, но рабочий.
ПС написано и проверено в Кодевижен.
задефайнены:
#define uint8_t unsigned char
#define PIN_WS2812 PORTB.0
вызов:Спойлер
Код: Выделить всё
for (i=0;i<sizeof(Chkala);i+=3) {Send(&Chkala[i]);};
где Chkala[] - массив с выводимыми данными
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 11:31:12
veso74
[uquote="КРАМ",url="/forum/viewtopic.php?p=4519552#p4519552"]Каким таким таинственным способом может быть реализована функция
_delay_us(0.4), например, если она не кратна 0,125?[/uquote]Ето 1 цикл (ticks). Пересчитывайте из библиотечного кода. Получается задержка МК в 1 такт.
Спойлер
Код: Выделить всё
void _delay_us(double __us)
{
double __tmp ;
#if __HAS_DELAY_CYCLES && defined(__OPTIMIZE__) && \
!defined(__DELAY_BACKWARD_COMPATIBLE__) && \
__STDC_HOSTED__
uint32_t __ticks_dc;
extern void __builtin_avr_delay_cycles(unsigned long);
__tmp = ((F_CPU) / 1e6) * __us;
#if defined(__DELAY_ROUND_DOWN__)
__ticks_dc = (uint32_t)fabs(__tmp);
#elif defined(__DELAY_ROUND_CLOSEST__)
__ticks_dc = (uint32_t)(fabs(__tmp)+0.5);
#else
//round up by default
__ticks_dc = (uint32_t)(ceil(fabs(__tmp)));
#endif
__builtin_avr_delay_cycles(__ticks_dc);
#else
uint8_t __ticks;
double __tmp2 ;
__tmp = ((F_CPU) / 3e6) * __us;
__tmp2 = ((F_CPU) / 4e6) * __us;
if (__tmp < 1.0)
__ticks = 1;
else if (__tmp2 > 65535)
{
_delay_ms(__us / 1000.0);
}
else if (__tmp > 255)
{
uint16_t __ticks=(uint16_t)__tmp2;
_delay_loop_2(__ticks);
return;
}
else
__ticks = (uint8_t)__tmp;
_delay_loop_1(__ticks);
#endif
}
8000000 / 3000000 * 0.4 = 1.0666 -> _delay_loop_1(1); будет 1 такт (tick).
А и каждое меньшее число чем 0.4 сокращается до 1 такт.
(Автор кода решает, нужно ли это. В данном случае я бы пропустил написание этой строки).
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 11:43:13
КРАМ
[uquote="veso74",url="/forum/viewtopic.php?p=4519622#p4519622"]Пересчитывайте из библиотечного кода. Получается задержка МК в 1 такт.[/uquote]
Не получается. Просто вызов функции и возврат из нее - это ЧЕТЫРЕ машинных цикла. То есть 0,5 мкс при 8 МГц системной частоты.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 11:45:12
veso74
Возможно. Переход с C на ASM мне не близок.
в других быстрых устройствах работает (напр. AD98XX) напрямую без задержек даже на 16, 32 MHz. Но WS2811 не отличается особой скоростью. Бы добавил целое число задержки.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 12:17:18
КРАМ
[uquote="veso74",url="/forum/viewtopic.php?p=4519633#p4519633"]WS2811 не отличается особой скоростью.[/uquote]Времена диаграммы автор озвучил. Можно сделать на nop-ах в АСМе, точно посчитав циклы.
Но лучше делать аппаратно на таймере, либо SPI. Скорость там достаточно высокая с учетом выбранной частоты работы МК.
[uquote="veso74",url="/forum/viewtopic.php?p=4519633#p4519633"]Возможно.[/uquote]
Тут есть два варианта.
Либо это макрос и его тело вставляется вместо функции, а содержимое считается не в рантайме, а на этапе компиляции. Тогда там можно откалибровать с точностью до 1 машинного цикла на основании дефайна системной частоты.
Либо это полноценная функция с передачей в нее аргумента и внутренним циклом. Тогда ни о каких единицах микросекунд речи быть не может.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 12:39:29
BOB51
Два простейших решения -
1. сделать проект под ардуиноIDE, спользуя библиотеку Adafruit_NeoPixel;
(в составе IDE достаточно "платформ" под атмегу8)
2. использовать ассемблер при максимально возможной частоте МК.
( к примеру таким фрагментом:
Спойлер
Код: Выделить всё
;
;
; trd2812_ma.txt
;
; файл обработчика передачи массива
; из буфера вывода в линейку на основе WS2812B
; базовый МК из линейки АТМЕЛ при тактовой частоте
; от 16 Мегагерц ( 0,000000062 S)
;
; требуемые интервалы по даташиту WS2812B
;
;Data transfer time( TH+TL=1.25µs±600ns)
; T0H 0 code ,high voltage time 0.4us ±150ns
; T1H 1 code ,high voltage time 0.8us ±150ns
; T0L 0 code ,low voltage time 0.85us ±150ns
; T1L 1 code ,low voltage time 0.45us ±150ns
; RES low voltage time Above 50µs
; исходный уровень линии связи = 0
; данные передаются пакетами из трех байт на точку
; старшими битами вперед в последовательности
; соответствующей G - R - B цветам точки
; количество блоков должно соответствовать
; количеству точек в ленте
;
; реальные данные согласно тест - отладки дебаггером (версия1!)
; авр-студио 4.19
;
; Data transfer time( TH+TL=1.38µs -10ns)
; T0H 0 code ,high voltage time 0.44us ±10ns
; T1H 1 code ,high voltage time 0.88us ±10ns
; T0L 0 code ,low voltage time 0.94us ±10ns
; T1L 1 code ,low voltage time 0.50us ±10ns
; RES low voltage time 192,88uS (Above 50µs)
;
; длина прерывания с пакетом загрузки (x60*3) = 2175uS (0.002175)
; интервал между прерываниями (irq t/c0) = 0.004S (4000uS)
;
; define datas
; .equ port_out = PORTB ; порт вывода (по усмотрению)
; .equ out_line = 0 ; линия вывода данных
; .equ bufout = SRAM_START ; начальный адрес буфера вывода
; .equ pixel = 60 ; количество точек в линейке/ленте
; .equ bufout_size = (pixel * 3) ; не может быть более объема ОЗУ - стек!!!
;таблица обьявленных имен - переназначение регистров РОН
;
; .def name = r31 ; ZH регистр (полный)
; .def name = r30 ; ZL регистр (полный)
; .def name = r29 ; YH регистр (полный)
; .def name = r28 ; YL регистр (полный)
; .def name = r27 ; XH регистр (полный) указатель текущей ячейки массива bufout
; .def name = r26 ; XL регистр (полный) указатель текущей ячейки массива bufout
; .def name = r25 ; регистр (полный) BH
; .def name = r24 ; регистр (полный) BL
; .def name = r23 ; регистр (полный)
; .def name = r22 ; регистр (полный)
; .def name = r21 ; регистр (полный)
; .def name = r20 ; регистр (полный)
; .def name = r19 ; регистр (полный)
; .def name = r18 ; регистр (полный)
; .def tmp1 = r17 ; регистр (полный) счетчик байт вывода
; .def tmp0 = r16 ; регистр (полный) буфер выводимого байта
; .def regn = r15 ; регистр (урезан)
; .def regn = r14 ; регистр (урезан)
; .def regn = r11 ; регистр (урезан)
; .def regn = r10 ; регистр (урезан)
; .def regn = r9 ; регистр (урезан)
; .def regn = r8 ; регистр (урезан)
; .def regn = r7 ; регистр (урезан)
; .def regn = r6 ; регистр (урезан)
; .def regn = r5 ; регистр (урезан)
; .def regn = r4 ; регистр (урезан)
; .def regn = r3 ; регистр (урезан)
; .def regn = r2 ; регистр (урезан)
; .def matr = r1 ; регистр (урезан) r1 по возможности не использовать!!!
; .def madr = r0 ; регистр (урезан) r0 по возможности не использовать!!!
;
;-----------------------------------------
; .macro ;; ввод и предобработка данных
;
;
; .endmacro
;
;-----------------------------------------
;
; определение буфера вывода в области данных
; .dseg
; .org bufout
;point0: .byte 3 ; g:r:b
;point1: .byte 3 ; g:r:b
;point2: .byte 3 ; g:r:b
;point3: .byte 3 ; g:r:b
;point4: .byte 3 ; g:r:b
;point5: .byte 3 ; g:r:b
;point6: .byte 3 ; g:r:b
;point7: .byte 3 ; g:r:b
;point8: .byte 3 ; g:r:b
;point9: .byte 3 ; g:r:b
;point10: .byte 3 ; g:r:b
;point11: .byte 3 ; g:r:b
;point12: .byte 3 ; g:r:b
;point13: .byte 3 ; g:r:b
;point14: .byte 3 ; g:r:b
;point15: .byte 3 ; g:r:b
;point16: .byte 3 ; g:r:b
;point17: .byte 3 ; g:r:b
;point18: .byte 3 ; g:r:b
;point19: .byte 3 ; g:r:b
;point20: .byte 3 ; g:r:b
;point21: .byte 3 ; g:r:b
;point22: .byte 3 ; g:r:b
;point23: .byte 3 ; g:r:b
;point24: .byte 3 ; g:r:b
;point25: .byte 3 ; g:r:b
;point26: .byte 3 ; g:r:b
;point27: .byte 3 ; g:r:b
;point28: .byte 3 ; g:r:b
;point29: .byte 3 ; g:r:b
;point30: .byte 3 ; g:r:b
;point31: .byte 3 ; g:r:b
;point32: .byte 3 ; g:r:b
;point33: .byte 3 ; g:r:b
;point34: .byte 3 ; g:r:b
;point35: .byte 3 ; g:r:b
;point36: .byte 3 ; g:r:b
;point37: .byte 3 ; g:r:b
;point38: .byte 3 ; g:r:b
;point39: .byte 3 ; g:r:b
;point40: .byte 3 ; g:r:b
;point41: .byte 3 ; g:r:b
;point42: .byte 3 ; g:r:b
;point43: .byte 3 ; g:r:b
;point44: .byte 3 ; g:r:b
;point45: .byte 3 ; g:r:b
;point46: .byte 3 ; g:r:b
;point47: .byte 3 ; g:r:b
;point48: .byte 3 ; g:r:b
;point49: .byte 3 ; g:r:b
;point50: .byte 3 ; g:r:b
;point51: .byte 3 ; g:r:b
;point52: .byte 3 ; g:r:b
;point53: .byte 3 ; g:r:b
;point54: .byte 3 ; g:r:b
;point55: .byte 3 ; g:r:b
;point56: .byte 3 ; g:r:b
;point57: .byte 3 ; g:r:b
;point58: .byte 3 ; g:r:b
;point59: .byte 3 ; g:r:b
;
;-----------------------------------------
.cseg
bptr0:
nop
.org (bptr0 + (256 - (bptr0 & 0x00FF)))
slot0:
; 6/14 (6-4=2 посему роль остатка выполняет CBI)
cbi port_out,out_line ; 2 цикла
nop
nop
nop
nop
nop
nop
nop
nop
nop
ret ; 4 цикла
.org (slot0+16)
slot1:
nop ; 13/7 (13-4=9)
nop
nop
nop
nop
nop
nop
cbi port_out,out_line ; 2 цикла
nop
nop
ret ; 4 цикла
;.org 0x0060
.org (slot1+16)
xslot0:
; 6/14 (6-5=1 посему роль остатка выполняет CBI с избытком в 1 nop)
cbi port_out,out_line ; 2 цикла
cbr zl,(1<<5) ; модификация указателя 1 цикл
nop
nop
nop
nop
nop
nop ; -2 цикла на ld tmp0,x+
dec tmp1 ; 1 цикл
brbs SREG_Z,ends_trd ; 1 цикл при неисполнении (в цикле)
rjmp trasstt ; 2 цикла
.org (xslot0+16)
xslot1:
nop ; 13/7 (13-5=8)
cbr zl,(1<<5) ; модификация указателя 1 цикл
nop
nop
nop
nop
nop
cbi port_out,out_line ; 2 цикла
; -2 цикла на ld tmp0,x+
dec tmp1 ; 1 цикл
brbs SREG_Z,ends_trd ; 1 цикл при неисполнении (в цикле)
rjmp trasstt ; 2 цикла
;
ends_trd:
pop tmp0
pop tmp1
pop xl
pop xh
pop zl
pop zh ; восстановить рабочую область из стека
ret
;-----------------------------------------
;
; предварительно:
; линия out_line настроена на вывод
; исходный уровень out_line =0
; указатель стека усатновлен на RAMEND
; массив данных (bufout:bufout_size) предварительно загружен
; флаг готовности массива данных установлен
;
mass_trm:
push zh
push zl
push xh
push xl
push tmp1
push tmp0 ; храним рабочую область в стеке
res_line:
ldi tmp0,4
ser tmp1
cbi port_out,out_line
res_time:
dec tmp1
brne res_time
dec tmp0
brne res_time ; =>50uS time out
ldi xh,high (bufout)
ldi xl,low (bufout) ; загрузка начального адреса массива
; в указатель
ldi tmp1,bufout_size
ldiw z,slot0 ; адрес начала таблицы в указателе
; ldiw z,(bptr0 + (256 - (bptr0 & 0x00FF)))
;-----------------------------------------
trasstt:
ld tmp0,x+ ; 2 цикла
slot_0:
sbi port_out,out_line ; 2 цикла реально до установки 3 цикла
bst tmp0,7 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла = 4 цикла от out_line=1
;----------------
slot_1:
sbi port_out,out_line
bst tmp0,6 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_2:
sbi port_out,out_line
bst tmp0,5 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_3:
sbi port_out,out_line
bst tmp0,4 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_4:
sbi port_out,out_line
bst tmp0,3 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_5:
sbi port_out,out_line
bst tmp0,2 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_6:
sbi port_out,out_line
bst tmp0,1 ; 1 цикл
bld zl,4 ; 1 цикл
icall ; 3 цикла
;----------------
slot_7:
sbi port_out,out_line
;sbr zl,(1<<6) ; модификация указателя под завершающий фрагмент
sbr zl,(1<<5)
; 1 цикл
bst tmp0,0 ; 1 цикл
bld zl,4 ; 1 цикл
ijmp ; 3 цикла
;----------------
)

Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 12:55:46
Starichok51
КРАМ писал(а):Просто вызов функции и возврат из нее - это ЧЕТЫРЕ машинных цикла
3 цикла на вызов (rcall) и 4 на возврат (ret). итого 7 циклов.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 13:41:52
КРАМ
[uquote="Starichok51",url="/forum/viewtopic.php?p=4519661#p4519661"]3 цикла на вызов (rcall) и 4 на возврат[/uquote]
Ну вооот, я то надеялся...
У меня перед носом лежит альбом с инструкциями АВР. Но я поленился посмотреть...

Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 13:58:43
>TEHb<
Использовал
вот эту библиотеку на тини13 с внутренним генератором на 9,6 МГц. Можно либо её вотпрямсразу использовать, либо посмотреть как там всё внутри устроено. Зависит от целей. Уйма условной компиляции на разные случаи, всё программно. Так что это как минимум возможно.
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 14:41:36
Ivanoff-iv
[uquote=">TEHb<",url="/forum/viewtopic.php?p=4519678#p4519678"]Так что это как минимум возможно.[/uquote]Та ну! у меня тини 13 ленту таскает, (и код отправки данных в ленту я привел), или это ничего не доказывает?

и на меге 8 на её 8 МГц тактовой тоже работает...
Добавлено after 53 seconds:
Re: Управление адресной светодиодной лентой с помощью atmega8
правда у меня тайминги приходится ручками подбирать...
Добавлено after 2 minutes 5 seconds:
Re: Управление адресной светодиодной лентой с помощью atmega8
причем можно хоть указатель на ОЗУ или флеш, хоть непосредственно данные в функцию пихать
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Ср дек 20, 2023 20:05:07
ciaas
Re: Управление адресной светодиодной лентой с помощью atmega
Добавлено: Сб дек 23, 2023 10:21:25
Just_Fluffy
Вообще для управления такими диодами удобно использовать SPI или UART.
Но бывают случаи, когда этот интерфейс недоступен. Тогда приходится дрыжками ногать.
В этом случае следует учесть, что 2811/2812 диоды требовательны к длительности именно высокого уровня. Низкий можно немного и растянуть. Главное, не дотягивать до 50 мкс - а то будет сброс ))
У меня на 8 МГц тиньке для управления одиночным диодом работает вот такой код (у тиньки ножек было мало и ножки с последовательными интерфейсами были заняты):
Код: Выделить всё
#define NOP asm volatile ("nop" : : )
static void decodeByte(uint8_t val, uint8_t* buff){
uint8_t mask = 128;
while (mask) {
*buff = val & mask;
buff++;
mask >>= 1;
}
}
void setLedColor(uint8_t R, uint8_t G, uint8_t B){
uint8_t arr[24];
decodeByte(G,&arr[ 0]);
decodeByte(R,&arr[ 8]);
decodeByte(B,&arr[16]);
cli();
GPIO_RESET_BIT(LED2812);
_delay_us(50);
register uint8_t* parr = arr;
for ( uint8_t i = 0; i < 24; i++) {
if (*parr++) {
// bit == 1
GPIO_SET_BIT(LED2812);
NOP;NOP;NOP;NOP;
GPIO_RESET_BIT(LED2812);
} else {
// bit == 0
GPIO_SET_BIT(LED2812);
NOP;
GPIO_RESET_BIT(LED2812);
NOP;
}
}
GPIO_SET_BIT(LED2812);
sei();
}
GPIO_SET_BIT(LED2812); и GPIO_RESET_BIT(LED2812); - это просто макросы вида PORTx |= (1 << bit) и PORTx &= ~(1 << bit)
Длительность нулевой единички получилась около 375 нс, единичной единички - около 875 нс. Длительность ноликов достигает почти 1 мс, но повторюсь, это не настолько критично, как жесткие тайминги единички. Мерялось лог.анализатором с частотой выборки 24 МГц (каждые 41.6 нс выборка).
Для управления несколькими диодами можно разложить сразу все цвета в массив (если хватит памяти), либо аккуратно раскладывать биты прямо на лету, изменив условие на if (*parr & mask), а в конце либо уменьшать маску, либо инкрементировать parr и заново задавая маску. NOP в нулевом состоянии в ветке else тогда надо убрать, там и так будет перебор за счет доп. условий.
Ну и можно посмотреть еще вот
тут - тоже управление диодами ногодрыгом.