Доброго времени дня! Помогите пожалуйста новичку. Пытаюсь управлять адресной светодиодной лентой (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); }
посмотри осцилом - что ты передаешь. будет три байтных посылок с зазорами. это не прокатит. растаскивай свои байты втрое - в битовую последовательность и передавай по SPI.
сперва передаем логическую единицу в течение 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); }
Это шуткатакой? Любезный, у вас частота ядра МК - 8 МГц. То есть всего ОДНА одноцикловая инструкция (а они далеко не все одноцикловые, особенно условное и безусловное ветвление кода) выполняется за 0,125 мкс. Каким таким таинственным способом может быть реализована функция _delay_us(0.4), например, если она не кратна 0,125? Каким таким образом вы полагаете исполнение цикла и управление портом за 0 мкс? С чего вы вообще взяли, что функция _delay_us() в состоянии оперировать дробным аргументом?
//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[] - массив с выводимыми данными
_________________ Просто не учи физику в школе, и вся твоя жизнь будет наполнена чудесами и волшебством Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Качественное и безопасное устройство, работающее от аккумулятора, должно учитывать его физические и химические свойства, профили заряда и разряда, их изменение во времени и под влиянием различных условий, таких как температура и ток нагрузки. Мы расскажем о литий-ионных аккумуляторных батареях EVE и нескольких решениях от различных китайских компаний, рекомендуемых для разработок приложений с использованием этих АКБ. Представленные в статье китайские аналоги помогут заменить продукцию западных брендов с оптимизацией цены без потери качества.
8000000 / 3000000 * 0.4 = 1.0666 -> _delay_loop_1(1); будет 1 такт (tick). А и каждое меньшее число чем 0.4 сокращается до 1 такт. (Автор кода решает, нужно ли это. В данном случае я бы пропустил написание этой строки).
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
в других быстрых устройствах работает (напр. AD98XX) напрямую без задержек даже на 16, 32 MHz. Но WS2811 не отличается особой скоростью. Бы добавил целое число задержки.
Времена диаграммы автор озвучил. Можно сделать на nop-ах в АСМе, точно посчитав циклы. Но лучше делать аппаратно на таймере, либо SPI. Скорость там достаточно высокая с учетом выбранной частоты работы МК.
Тут есть два варианта. Либо это макрос и его тело вставляется вместо функции, а содержимое считается не в рантайме, а на этапе компиляции. Тогда там можно откалибровать с точностью до 1 машинного цикла на основании дефайна системной частоты. Либо это полноценная функция с передачей в нее аргумента и внутренним циклом. Тогда ни о каких единицах микросекунд речи быть не может.
Два простейших решения - 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) ; не может быть более объема ОЗУ - стек!!!
Использовал вот эту библиотеку на тини13 с внутренним генератором на 9,6 МГц. Можно либо её вотпрямсразу использовать, либо посмотреть как там всё внутри устроено. Зависит от целей. Уйма условной компиляции на разные случаи, всё программно. Так что это как минимум возможно.
Та ну! у меня тини 13 ленту таскает, (и код отправки данных в ленту я привел), или это ничего не доказывает? и на меге 8 на её 8 МГц тактовой тоже работает...
Добавлено after 53 seconds: Re: Управление адресной светодиодной лентой с помощью atmega8 правда у меня тайминги приходится ручками подбирать...
Добавлено after 2 minutes 5 seconds: Re: Управление адресной светодиодной лентой с помощью atmega8 причем можно хоть указатель на ОЗУ или флеш, хоть непосредственно данные в функцию пихать
_________________ Просто не учи физику в школе, и вся твоя жизнь будет наполнена чудесами и волшебством Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Вообще для управления такими диодами удобно использовать SPI или UART. Но бывают случаи, когда этот интерфейс недоступен. Тогда приходится дрыжками ногать.
В этом случае следует учесть, что 2811/2812 диоды требовательны к длительности именно высокого уровня. Низкий можно немного и растянуть. Главное, не дотягивать до 50 мкс - а то будет сброс ))
У меня на 8 МГц тиньке для управления одиночным диодом работает вот такой код (у тиньки ножек было мало и ножки с последовательными интерфейсами были заняты):
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 тогда надо убрать, там и так будет перебор за счет доп. условий.
Ну и можно посмотреть еще вот тут - тоже управление диодами ногодрыгом.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 34
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения