Игрушка для ребенка. Кубик
- Войдите на сайт для отправки комментариев
Решил сделать игрушку ребенку.
Куб. 6 сторон соответственно. в каждой по 4 светодиода, подключенных через полевой транзистор
также ик диод и ик фототранзистор в качестве датчика
смысл следующий. обрабатываем датчики. и при определенных условиях включаем нужную сторону или по кругу зажигаем светодиоды
если хватит места еще плата с mp3 плеером чтобы звуки воспроизводить
итого 24 светодиода, 6 датчиков. для каждой стороны своя плата. + плата управления+преобразователь+ литиевый аккумулятор = 8 плат. если плерр влезет получится 9 плат
предлагаю присоеденится по желанию. необходима помощь в алгоритмах. ведь будет срабатывание и на поверхность на которой лежит и на руки и тп. датчику пофигу
буду рад любым предложениям
ну и фото. вырезали сегодня на станке
изнутри покрашу черным


я бы акселерометры установил по трём плоскостям, а по сторонам по одному RGB светодиоду - достаточно будет для индикации оттенков цвета и реакции сторон на повороты в пространстве, встряхивания, жесты...
*24 светодиода - недоерунда получится, да и батарею сожрёт быстро.
чего-то вспомнилось "а хотите я его стукну? он станет фиолетовый в крапинку!"
а вообще идея интересная. насчет быстро батарею сожрет... на пару часов в любом случае хватит, а этого наиграться достаточно. только надо разъем для зарядки вывести на корпус, чтобы не разбирать.
и по поводу корпуса - а чего не вырезали сразу из непрозрачного пластика, цветного какого-нибудь, чтобы не красить изнутри?
красить нужно было чтобы свет одной стороны не светил на другие
да и непрозрачного пластика не было приемлемой толщины, а искать не хотелось. сделал из того что было
я покрасил. свет не распространяется равномерно, чтобы равномерно надо с торца засвечивать
rgb светодиодов под рукой не было, поставил зеленые
акселерометр есть mpu6050? может приделаю
проверил датчики приближения. реагируют где то сантиметров с 5. до этого только вплотную реагировали, пришлось увеличить ток ик диодов
насчет батареи: постоянно светятся только ик диоды, остальные будут включаться по условиям, да и то не все
плату зарядки тоже засуну если влезет
какого размера кубик, а то по фото трудно оценить?
забыл написать
внутренний размер 50х50х50мм
наружний 58х58х58мм
вопрос. кто тестировал либу SoftPWM. там нет каких нибудь глюков или ограничений?
нужно 6 каналов шима просто, а в меге8 только 3 хардварных
вопрос. кто тестировал либу SoftPWM. там нет каких нибудь глюков или ограничений?
Не тестировал, но "а шо ей станется?". Даже если есть подглюки с таймингами, то учитывая вашу задачу заметить их "на глаз" - никаких шансов. Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.
А еще, учитывая размеры куба (место не нужно жестко экономить) можно посмотреть на что-то типа TLC5940
Драйвер светодиодов. 16-ти канальный. С PWM. Поддерживает каскадирование (можно взять несколько штук и подключить их одну за другой, не увеличивая количество занятых ног камня). Умеет сам выдерживать стабильный ток на светодиод (от 0 до 120ma задать можно) - не нужно каждому светику свой резистор лепить, имеет Dot Correction (правда у меня пока не заработал толком, но не больно и нужно в данный момент :), контроль по температуре и обрыву светодиодов.
Вообщаем так сказать "запас на будущие" (вдруг после сборки захочется побольше светодиодов, или те 6-ть которые есть, захочется сделать RGB что-бы куб переливался).
Единсвенное что - библиотека его. "Под капотом", по крайней мере мне, она ОЧЕНЬ не нравится. То ли автор настолько крут что я его не понимаю, но IMHO наверченно много лишнего и конфигурение сделано через ж....
Из серьезных минусов - библиотека забирает "под себя" два таймера (IMHO можно было-бы и одним обойтись).
ну и Dot Correction, по крайней мере у меня на MEGA работать не захотел. На более младших камнях - пока не пробовал.
P.S. Dot Correction - это компенсация разницы яркости светодиодов. К примеру синие светят ярче чем красные, и не хочется каждый раз учитывать это в коде. Можно зашить в память микрухи "поправку на ветер", и в коде считать их "одинаковыми".
хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет
задачу с токами я упростил. падение напряжения на зеленом светодиоде (которые у меня валяются) около 2,5В. и я просто поставил 2 последователь. проверил ток. все норм даже если просто 5В подать.
поставиk полевик irlml2502. пока все норм. надо вставлять платы, склеивать корпус, делать плату управления и запихивать все внутрь. платы для каждой стороны уже собраны
кстати пока баловался с платой (тестил токи, датчики) если затвор полевика не подтянуть на землю реагирует на прикосновение пальца. только как тиристор включился и не выключается. Но хотя бы выключается пока не тем же пальцем не замкнешь на землю. вот думаю сенсор попробовать сделать отдельно. получится ли добится стабильной работы. если да то пригодится
И еще, а зачем мучать себя "как при царском режиме"? Это я про Atmega8 в качестве целевого.
Почему не Atmega328? Оно очень неприятно, под конец проекта уперется в "ой памяти не хватает, что делать?"
Если планируется крупная серия девайсов - тогда понятно желание экономить. А на одной штучке - никакого резона.
А нас 328 стоит на $0.7 дороже восмерки. Бывают периоды когда 328 даже дешевле оказываются.
328 в наличии только в платах ардуины. игрушка будет одна для ребенка, никакой партии
мне кажется что памяти хватит и много останется. нечему там особо память кушать. сложных алгоритмов не предполагается
половина наверно будет пустая. но если реально не хватит ардуину внутрь засуну. обещаю
вот думаю над алгоритмами. думаю пока так
1. 1 приближение и убрали сторона загорелась и выключилась (плавно)
2. два быстрых как бы хлопка по стороне. стороны вокруг этой стороны "крутятся" и музыка включилась
надо может еще что нибудь. хотя наверно это уже в собранном виде при игре будет понятнее
хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет
Да. Наличие это для них дейсвительно проблема. Зато в остальном... только на полевиках сколько места можно съкономить.
И резисторы токоограничивающие не нужны (да я видел что вы решили проблему "последовательностью включения", но лично мне такое решение не очень нарвится :)
мне кажется что памяти хватит и много останется. нечему там особо память кушать. сложных алгоритмов не предполагается
Ну хозяин барин. А память кончается всегда "неожиданно", причем проявляется это совсем приятно. Никаких ошибок "память кончилась" - тут не предвидится. Просто "начинает глючить" (перегружается, бред в переменных) и вы недельку тратите что-бы понять в чем проблема (вначале-то думаете что проблема у вас в коде или библиотеки глючные). Проблема с нехваткой места которая обнаруживается при заливке (скетч не влазит) - это если повезет. Нехватка RAM - вот чего боятся нужно.
А выжрать память и без алгоритмов хитрых - "как два пальца обо...".
Подключил парочку библиотек, натыкал Serial.println для дебага, объявил какой-нибудь массивчик типа long побольше размером и..... готово :)
Вообщем мне проще переплатить $0.7 и чувствать себя более спокойно :)
freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }Во всех скетчах использую, чтобы без неожиданностей - возвращает количество свободной оперативы
хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет
Да. Наличие это для них дейсвительно проблема. Зато в остальном... только на полевиках сколько места можно съкономить.
И резисторы токоограничивающие не нужны (да я видел что вы решили проблему "последовательностью включения", но лично мне такое решение не очень нарвится :)
как любил говорить мой начальник дешево и сердито. это я про последовательное включение. так у меня есть max7219. но разводка с ней в корпусе уж больно жесткая будет изза матричного подключения светодиодов
а насчет полевиков тут не согласен. SOT23 очень маленький корпус, даже 6 таких корпусов будут скорее всего меньше
лешак. как только будет мало места сразу вставлю плату ардуины (хоть и не хочется) ну нет у меня в наличии 328 меги отдельной. в кварц поблизости можно было бы сгонять, но они даже за мегу 88 в дип корпусе 600 рублей хотят. совсем обнаглели
freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }Во всех скетчах использую, чтобы без неожиданностей - возвращает количество свободной оперативы
спасибо за код. я раньше его юзал когда массивы для символов жк дисплея в опертивке хранил. вот я намучался с нехваткой оперативки. буду навсякий случай проверять
вот думаю над алгоритмами. думаю пока так
1. 1 приближение и убрали сторона загорелась и выключилась (плавно)
2. два быстрых как бы хлопка по стороне. стороны вокруг этой стороны "крутятся" и музыка включилась
надо может еще что нибудь. хотя наверно это уже в собранном виде при игре будет понятнее
загорается произвольная сторона, нужно повернуть горящими диодами вверх за 0.хх секунд и поставить на стол, успел - веселая музыка, не успел - грустная, и так много раз подряд.
зы. если договориться, что переворачивать кубик можно только носом, то получится неплохой аттракцион для подвыпившей компании взрослых :)
)))) прикольно. надо будет сделать
еще надо будет попробовать звук генерить, на меге8 наверно не получится. таймеров не хватит, но вообще интересно
имперский марш))
http://proapi.ru/melodiya-na-arduino-uno-r3/
но они даже за мегу 88 в дип корпусе 600 рублей хотят. совсем обнаглели
Ох нифига себе... У нее что выводы платиновые? У нас за эти деньги можно Nano(клон) или Uno(клон), или Mini оригинал, или две Pro Mini (клон).
Ну вообщем понятно, вам лучше исходить из того что есть в наличии :)
Просто при следующих закупках стоит подумать "что лучше брать.."
Я, в свое время, тоже закупился Atmega8, про запас, и только много позже понял что это был "не самый оптимальный выбор".
при следующей закупке куплю 328. бывает острая необходимость
да у нас тоже можно купить. в митино все гораздо дешевле, но туда долго добираться. полтора часа в одну сторону
+ видно цены немного упали. а меги 88 вообще нет
328 вообще нет.
мега32а смд стоит 150 рублей (самое дешевое что есть)
а мега8а дип 280 рублей. где логика
мега16 в дип 900 рублей)))
блин вот я бился бился с звуковым модулем, а потом почитал даташит. он гигабайтные флешки максимум поддерживает
я про WTV020-SD. блин и как назло минимум флешки 2гб. облом звука не будет
+ уменьшил размер раздела до 900. сконвертировал в wav 12кгц, название песни по английски на всякий. не помогло к сожалению
все ставлю arduino micro в игрушку пока не придут 328 меги. нужно 6 шимов. хотел сделать софварный на меге8, но там таймеров не хватает. статьи встречаются про софтварный шим на меге8, но либо написаны на асме, либо на баскоме, либо вообще что то не вменяемое. нашел приличное видео чувак сделал софтварный шим на 12 каналов вроде, но без ссылки на источник
блин на тиньке 13 делают софтварный шим, а тут на меге проблема с исходниками
Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.
может я что то путаю. давненько читал. надо перечитать. но вроде не получится на меге8 его использовать. наверно путаю
лашак подскажите пожалуйста. библиотека timerone работает на 1 таймере. этот таймер есть в меге8. но компилятор ругается на пример из библиотеки. сам не понял что нужно исправлять
Попробуйте, в файлик TimerOne.cpp
После строчки
Между строками
Воткнуть вот это:
P.S. Похоже, таки авторы библиотеки не сделали поддержку ATMEGA8 :( Этот кусок первое что "нагуглилось". Особо не вникал в него. Если он не поможет (компилироваться будет, а вот работает ли - не на чем проверить), тогда таки прийдется раскуривать даташит и руками самому конфигурить таймер, без библиотеки..
скомпилировалось. работает или нет дома уже проверять. спасибо. я много страниц пересмотрел. не попадалось такого вроде
проглядел наверно
В принципе более оптимальным (что-бы самому заново не реализовывать) было бы "дофиксать", саму SoftPWM библиотеку.
ПО идее у нее в SofPWM_timer.h
Нужно написать инициализацию для атмега8
Сделать atmega8 вот этих трех дефайнов
#define SOFTPWM_TIMER_INTERRUPT TIMER2_COMPA_vect #define SOFTPWM_TIMER_SET(val) (TCNT2 = (val)) #define SOFTPWM_TIMER_INIT(ocr) ({\ TIFR2 = (1 << TOV2); /* clear interrupt flag */ \ TCCR2B = (1 << CS21); /* start timer (ck/8 prescalar) */ \ TCCR2A = (1 << WGM21); /* CTC mode */ \ OCR2A = (ocr); /* We want to have at least 30Hz or else it gets choppy */ \ TIMSK2 = (1 << OCIE2A); /* enable timer2 output compare match interrupt */ \ })Открыть даташит на Atmega8, читать "коментарий справа" и смотреть какими эти регистры называются в ATMEGA8 , у них просто чуток другие имена.
Скажем у Atmega328 он называется TIMSK2, а у Atmega8 он же называется просто TIMSK . Вообщем просто нужно сесть и "внимательно вкурить". Но на это сейчас, к нема времени. А готовый кусок, к сожалению, нагуглить не вышло.
я пытался разобраться. но пока эти таймеры для меня темный лес
читаю даташит но пока не понял почему например регистр SFIOR
блин часа два уже точно ломаю голову. предложенная вставка работает, но только на выводах с хардварным шимом. на других нет
как говорится печалька
и столько "редисок" на ютубе. видео выкладывают без ссылки на источник. там и 12 канальный шим делали
>блин часа два уже точно ломаю голову. предложенная вставка работает,
Какая предложенная вставка? Для TimerOne? Так а потом как вы pwm делаете? Через Timer1.pwm? или через Timer1.attachInterrupt()?
через Timer1.pwm
вставка эта
Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.
"самому дергать ноги" - это значит самому реализовать програмный шим. А TimerOne - только для того, что-бы не разбиратся с "пока эти таймеры для меня темный лес". Она обеспечивает вам только "вызов вашей функции с заданной переодичностью".
А в "реализовать програмный шим" тоже ничего сверсложного нет. Инициализируете таймер числом поменьше (что-бы частота pwm-ма была повыше). И дальше как-то так:
byte levels[TOTAL]; // какой уровень "яркости" на каждом канале. 255 означает 100% byte pins[TOTAL]; // пины соотвествующие каждому каналу volatile byte pwm_cnt=0; void timer_actino(){ for(byte i=0;i<TOTAL;i))digitalWrite(pins[i], pwm_cnt<levels[i]); pwm_cnt++; }Ну как-бы и все. Дальше все аналогично обычному ШИМу. Только вместо analogWrite(УРОВЕНЬ), пишите levels[НОМЕР-КАНАЛА]=УРОВЕНЬ.
Ну и, конечно, я упростил немного. Что-бы идею проще объяснить. Использовал digitalWrite для дрыгания ногами. Возможно он слишком тормознутый будет для этого. Особенно если каналов будет много. Тогда дрыгать через Прямое управления выходами через регистры микроконтроллера Atmega
не особо понял
предполагаю так
#include <TimerOne.h> void setup() { byte levels[TOTAL]; //тут мне что писать? у меня 6 каналов. соответственно 6 значений должно быть //byte levels[0,0,0,0,0,0]; //или оставить как есть и добавить переменну TOTAL равную 6 byte pins[TOTAL]; // пины соотвествующие каждому каналу //byte pins[5,6,7,8,9,10]; //тут наверно все такие пины прописать volatile byte pwm_cnt=0; } for (byte i=5;i<11;i++){ pinMode(i, OUTPUT); } Timer1.initialize(1000); Timer1.attachInterrupt(timer_actino); } void loop() { levels[0]= 128; } //но тогда непонятно как подпрограмма будет работать. цикл работает от 0 до значения TOTAL //если оставить сверху TOTAL void timer_actino(){ for(byte i=0;i<TOTAL;i))digitalWrite(pins[i], pwm_cnt<levels[i]); pwm_cnt++; }больше всего непонятна эта запись
наверно что то вроде этого
void timer_actino(){ for(byte i=0;i<TOTAL;i){ digitalWrite(pins[i], pwm_cnt<levels[i]);//пока pwm_cnt меньше заданного уровня результат //сравнения будет равен 1 и на выходе будет 1 } pwm_cnt++; }но непонятна эта запись
for(byte i=0;i<TOTAL;i){ //i=0, и пока ш меньше TOTAL, i. что делает просто i? обычно же i++ или i--спасибо Лешак. долго гуглил, но инфы нормальной так и не нашел
+ думаю должно выглядеть так
#include <TimerOne.h> #define TOTAL 6 byte levels[TOTAL]; byte pins[] = {5,6,7,8,9,10}; volatile byte pwm_cnt=0; void setup() { for (byte i=5;i<=10;i++){ pinMode(i, OUTPUT); } for (byte i=0;i<=5;i++){ levels[i] = 0; //заполним массив для начала нулями } Timer1.initialize(1000); Timer1.attachInterrupt(timer_actino); } void loop() { levels[0]= 128; } void timer_actino(){ for(byte i=0;i<TOTAL;i))digitalWrite(pins[i], pwm_cnt<levels[i]); pwm_cnt++; }переделал по управление портами. вроде должно быть правильно. что скажете Лешак
до меня кажется дошло почему просто использовать прерывание и ногодрыганьем делать шим
#include <TimerOne.h> #define TOTAL 6 byte levels[TOTAL]; volatile byte pwm_cnt=0; void setup() { DDRB = DDRB | B00111111; //настраиваем порт на выход //ардуиновские выводы 8-13 (проще когда выводы на одном порту) for (byte i=0;i<TOTAL;i++){ levels[i] = 0; //заполним массив для начала нулями } Timer1.initialize(1000); Timer1.attachInterrupt(timer_actino); } void loop() { levels[0]= 128; } void timer_actino(){ for(byte i=0;i<TOTAL;i++){ if(pwm_cnt<levels[i]){ PORTB |= 1<<i; } else{ PORTB &= ~(1<<i); } } pwm_cnt++; }for(byte i=0;i<TOTAL;i){ //i=0, и пока ш меньше TOTAL, i. что делает просто i? обычно же i++ или i--
Конечно. Вы правы. Должно быть i++. Очепятка.
спасибо Лешак. долго гуглил, но инфы нормальной так и не нашел
А кто вам сказал, что для любой проблемы есть ответ в гугле? ;) К сожалению, система образования приучает нас к этому. Есть в учебник (или гугл), в котором есть ответы на все типовые вопросы. Нужно их только "заучить" и получить максимальный бал на экзамене. А когда, потом, человек приходит на работу - оказывается что нужно как раз то за что "выгоняли с экзамена": пользуйся любой литературой, общайся с кем хочешь.... но для самых интерестных-оплачиваемых задач - ответа готового вообще нет. Его нужно придумать/найти. И в этом же самый кайф ;) Вот раскрой своего кубика, вы в гугле искали или почесали в затылке, прикинули так/этак и нарисовали?
Кстати... ответ в гугле - таки есть. Выше - я упоминал про микруху TLC5940. Если бы из любопыства почитали что это за зверь (только детально, а не "берем микруху, берем готовую либу... профит"), то увидили бы, что я просто програмно реализовал програмно то что делает эта микруха. А сама "идея" описана в даташите.
P.S. Это я не на вас бурчал, а на "систему образования в общем..." ;) Вы, в принципе, все делаете правильно. По крайней мере с моей точки зрения :) А она - тоже не истина в последней инстанции ;)
>вроде должно быть правильно. что скажете Лешак
А зачем меня спрашивать? Спросите ардуину. Или в думаете у меня в голове есть Avr-эмулятор? Есть конечно, но качество в любом случае похухе чем у настоящего :)
>Timer1.initialize(1000);
Я же говорил: "берите поменьше".
Ну давайте прикинем что у вас получилось. "Счетчик" будет тикать один раз в миллисекунду. Тикает он от 0 до 255. Значит "один оборот" происходит за 255ms. То есть, диод будет делать вкл/выкл 1000/255 ~=4 раза в секунду. Думаю такое мерцание на глаз не заметить может только слепой. У вас получился ШИМ с частотой ~4 герца. Дефолтный апаратный ШИМ, если память не изменяет, имеет частоту 497 Hz или 1 kHz (в зависимости от пина). Почувствуйте разницу.
for(byte i=0;i<TOTAL;i++){ if(pwm_cnt<levels[i]){ PORTB |= 1<<i; } else{ PORTB &= ~(1<<i); } }Кстати, для ускорения, можно состояние PORTB заранее расчитать. В момент установки levels[НОМЕР_КАНАЛ] делать перерасчет, массива, скажем port_states[]
Тогда сам обработчик, сократится до
void timer_action(){ PORTB=port_states[pwm_cnt]; pwm_cnt++; }Или вообще в одну строчку:
void timer_action(){ PORTB=port_states[ pwm_cnt++];}Тогда у нас не нужно будет делать if, для каждого канала, на каждую сработку таймера.
ну раскрой придумать гораздо проще чем программный шим. лично для меня
как только не тестил. была фигня. решил что сразу много пинов это слишком. надо начать с одного
#include <TimerOne.h> volatile byte pwm_cnt=0; byte levels; void setup() { pinMode(5, OUTPUT); Timer1.initialize(255000); Timer1.attachInterrupt(timer_actino); } void loop(){ levels= 250; } void timer_actino(){ for (pwm_cnt=0;pwm_cnt<256;pwm_cnt++){ if (pwm_cnt<=levels){ digitalWrite(5, 1); } if (pwm_cnt>levels){ digitalWrite(5, 0); } } }тусклоо горит. регулировка яркости результат не дает. вот думаю в чем дело. если частота слишком низкая, код в подпрограмме выполняется очень быстро. и результирующий шим получается все равно маленький.
без осциллографа не разобраться. подцепился DSO nano. тормоз одно слово. при большом увеличении ловит импульс, точнее начало импульса. завтра на работу возьму. будет проще разобраться. и есть ли там вообще шим
>ну раскрой придумать гораздо
Да ничего подобного. Раскрой что-то не додумал - расход материала и времени. А програмно - пробуй сколько хочешь. Попробовал так, попробовал этак.... Вообщем тут вопрос только "насколько часто вы это делаете".
Кстати один из способов прокачать этот навык "помощь на форуме" :)
>тусклоо горит.
Тут больше похоже что с железом. Судя по вашему коду - оно вообще должно гореть почти постоянно. А если просто на этой ноге включить светик, без всяких шимов. Он вообще горит?
И зачем два if-а? Вариант с else - был вполне себе нормальным.
>без осциллографа не разобраться. подцепился DSO nano
Погуглите есть OpenSource клиенты логического анализатора. И соотвествующий скетч для ардуины. Гуглите "arduino logic analyzer sump"
>будет проще разобраться.
Щас попробую своим глянуть, что оно у вас дает...
Но естетсвенно будет давать "не то". Так как вы послушали "Я же говорил: "берите поменьше". И сделали в точности наоборот.
Ой.ой... а зачем же вы в timer_action запихнули for, раз у вас остался один канал? Ну кончно работать не будет. Вы же на каждую сработку таймера теперь пробегаете сразу все значения pwm_cnt, естественно фигня выходит.
Если вы хотели "оставить один канал", то вам нужно было взять #32, переименовать levels в level (ну и естественно не массивом), а сам for просто выбросить из обработчика. for был нужен только что-бы "пробежатся по всем каналам". Если канал остался один - то собственно и for не нужен.
Ну вообщем взял базовый пример:
Arduino - Fading
Втыкнул в него наш таймер и дергание ногой. (только внимание, там D9 используется. И в примере и у меня на плате уже там висел светик).
Ну и как ожидалось диод замигал "что дурной". И это хотя я взял Timer1.initialize(1000); (про 255000 - я вообще думать не хочу)
Уменьшил до Timer1.initialize(10); - ну вообщем на глаз четко видно что PWM. Только загорается, разгорается чуток в другом темпе (ну или кажется мне). Сделали вместо digitalWrite прямую запись в порт. Все больше ни вижу на глаз разницы между тем как работало с analogWrite (а анализатор подключать - меня все-таки обломало).
#include <TimerOne.h> int brightness = 0; // how bright the LED is int fadeAmount = 5; // how many points to fade the LED by byte level=0; volatile byte pwm_cnt=0; void setup() { // declare pin 9 to be an output: pinMode(9, OUTPUT); Timer1.initialize(10); Timer1.attachInterrupt(timer_action); } void loop() { // analogWrite(9, brightness); level=brightness; brightness = brightness + fadeAmount; if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount ; } delay(30); } void timer_action(){ // digitalWrite(9, pwm_cnt<level); bitWrite(PORTB,1,pwm_cnt<level); // D9, PB1 pwm_cnt++; }>а анализатор подключать - меня все-таки обломало
Или не обламало. Вот при .initialize(8);
При уровне level=100. Тоесть яркость 100/255=0,39=39%
Иммем:
Верхняя пила - наш софтовый шим. Нижняя - аппартынй, через analogWrite(3,100)
BTW: а для одгоно пина, если больше ничего делать не нужно, можно вообще не заморачиватся тупо берем:
Arduino - SecretsOfArduinoPWM
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz digitalWrite(13, LOW); delayMicroseconds(1000 - 100); }Это я к тому, что "нигде не описанно" ;)
спасибо. разжевали
>А если просто на этой ноге включить светик, без всяких шимов
да работает. и светодиод на всякий случай постоянно проверял
>И зачем два if-а? Вариант с else - был вполне себе нормальным.
уже начал сомневаться, сделал на всякий случай. до этого был else
>Погуглите есть OpenSource клиенты логического анализатора. И соотвествующий скетч для ардуины. Гуглите "arduino logic analyzer sump"
хорошо почитаю
>Но естетсвенно будет давать "не то". Так как вы послушали "Я же говорил: "берите поменьше". И сделали в точности наоборот.
ставил и больше и меньше. но 10 не пробовал ставить
>Ой.ой... а зачем же вы в timer_action запихнули for, раз у вас остался один канал?
я предполагал работает так.
через определенные промежутки времени запускаем подпрограмму. промежутки времени соответствууют периоду колебаний. в подпрограмме делаем цикл до максимального значения шим. сравниваем необходимое значение счетчика с необходимым значением шим, пока меньше на выходе "1", стало больше на выходе "0"
>Уменьшил до Timer1.initialize(10); - ну вообщем на глаз четко видно что PWM
попробую что у меня получится
работает)))) спасибо еще раз
понял в чем моя ошибка. при прерывании запускается подпрограмма. в подпрограмме получился как бы счетчик pwm_cnt от 0 до 255 из-за типа данных byte. только сейчас понял. а я делал в подпрограмме счетчик отдельный.
у меня за одно исполнение подпрограммы происходил счет от до 255, а у вас с каждым прерыванием увеличивается на 1.
мой способ тоже рабочий. только параметры нужно подобрать и добавить задержки в цикле. только он ничем не лучше чем
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz digitalWrite(13, LOW); delayMicroseconds(1000 - 100); }по сути тоже самое. зато теперь понятнее стало
а про функцию bitWrite() я не знал. теперь буду знать
уже поздно. завтра попробую сразу на нескольких пинах сделать
. в подпрограмме делаем цикл до максимального значения шим.
Зачем? У нас же цикл сразу "Раззззз и прокрутился весь". А нам нужно что-бы он крутился с заданной скоростью. Сам вызов обработчика прерывания по таймеру это уже "как-бы цикл". Только тело цикла вызывается с некоторой переодичностью.
Цикл нам нужен только если каналов много. Что-бы пробежатся по каждому каналу выставить ногу в нужное положение. Но опять-таки в этом цикле, которым мы бежим по каналам, сам счетчик pwm_cnt - мы не трогаем. Он у нас меняется только один раз за одну сработку таймера. А его "обнуление" достигается за счет эффекта переполнения. Поэтому и нет необходимости делать проверки на "он достиг максимального значение". Он сам сбросится в ноль.
Ну если и так не понятно, то.... ну сделайте себе "виртуальный осцилографф".
byte pwm_cnt; byte level; void setup(){ Serial.begin(57600); level=100; } void loop(){ // Тут мы имитируем вызов timer_action() по таймеру. // Только частоту делаем очень маленькой, что-бы успевать в Serial пропихивать delay(200); timer_action(); } void timer_action(){ Serial.print(pwm_cnt); // Выводим текущие показание счетчика Serial.print(":"); // выводим в сериал "состояние пина" if(pwm_cnt<level){ Serial.println("*****************");// изображаем HIGH на пине } else { Serial.println(); // изображаем LOW; } pwm_cnt++; }Вообщем было-бы желание, даже с голой ардуиной без внешнего железа и измерительных можно со многим разобратся ;)
да я уже понял свою ошибку. написал уже в предыдущем сообщении
кстати раньше не знал что в значения передаваемые можно ставить условия. теперь узнал что если условие выполняется результат равен 1, если нет 0
проверил с 6 каналами. работает отлично))
#include <TimerOne.h> #define TOTAL 6 byte levels[TOTAL]; byte level=0; int brightness = 0; int fadeAmount = 5; volatile byte pwm_cnt=0; void setup() { DDRB = B00111111; for (byte i=0;i<=5;i++){ levels[i] = 0; //заполним массив для начала нулями } Timer1.initialize(10); Timer1.attachInterrupt(timer_action); } void loop() { for (byte i=0;i<TOTAL;i++){ levels[i] = brightness; } brightness = brightness + fadeAmount; if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount ; } delay(5); } void timer_action(){ for(byte i=0;i<TOTAL;i++)bitWrite(PORTB,i,pwm_cnt<levels[i]); pwm_cnt++; }Лешак подскажите пожалуйста правильно ли я понял
brightness = brightness + fadeAmount; if (brightness == 0 || brightness == 255) { fadeAmount = -fadeAmount ; }1. увеличиваем яркость на заданный шаг
2. при крайних значениях яркости меняем знак у яркости. но так как в шим заложен тип данных byte минус сам отсекается
ну все что нужно было сделано
делаю плату управления, собираю кубик
ну все что нужно было сделано
Вот поэтому я постоянно и твержу: перед тем как братся за "супер-проекты" нужно построчно вкиснуть в базовые примеры. Прямо в ArduinoIDE, делаем File/Examples/Basics/Fade
И другие примеры тоже посмотреть стоит. Не сейчас, так потом пригодится. Идеи/подходы.
проверил с 6 каналами. работает отлично))
1. Что-бы "работало долго и счастливо" стоит еще открыть даташит на камень и найти какой максимальный ток можно брать с одного пина и СУММАРНО с пинов подключеных к одному порту (естьи еще и третье ограничение - со всех пинов вместе взятых). И убедится что вы их не превышаете.
2. Если этот код выше "только проверить", то все нормально. Если так и будет "в итоге", то не понятно. Зачем вам массив channels[] если у него всегда все элементы имеют одинаковое значение. Можно и одной переменной level обойтись. После чего возникнет следующий вопрос: если у нас все светики "работают синхронно", то на кой кляп нам вообще нужно занимать столько ног, городить програмный шим и т.п. Хватит одной ноги и плебейского analogWrite на ногу с аппаратным шимом.
Лешак подскажите пожалуйста правильно ли я понял
Не нужно спрашивать. Натыкайте Serial.println-нов, выведите переменный в Serial и сами посмотрите что с ними происходит.
2. при крайних значениях яркости меняем знак у яркости. но так как в шим заложен тип данных byte минус сам отсекается
Нет. Если мы в одном месте использовали переполнение, это не значит что нужно видеть переполнение везде и всюду.
> меняем знак у яркости.
Возмите словарик и переведите слова brightness, fade, amount. Посмотрите внимательно у чего именно мы меняем знак. А еще лучше откройте оригинальный fade-пример (через ардуиноIDE или по ссылке которую я вам давал). И почитайте коментарии к каждой строке (там они более подробны, я их потер для экономии экранного-места).
ток не превысит. на каждой стороне свой полевик уже стоит, платы для сторон собраны.
нужно было только считывание с датчиков, это не проблема. ну и управление с шимом. теперь это тоже сделано
каналов нужно именно 6. каждая сторона будет светится независимо, просто во время тестов сделал все одинаковым
проект несложный. меги328 просто в наличии не было. уже заказаны. покинули почту китая
усложнилось из-за программного шима, так как в меге8 не хватает аппаратных шимов
я уже собирался туда ардуину вставлять на время, пока 328 не придут
но вы помогли и ставлю мегу8. потом все равно заменю на 328, нужно будет еще кое что добавить, но на меге8 это уже не вариант делать
а насчет почитать. да надо читать. знаний не хватает. я обычно для этого просто делал 2 цикла "вверх" и "вниз"
но ваше решение компактнее. прочитал еще раз и понял как он работает. как все просто
Ну если нужно все-таки отдельно, то я бы на вашем месте, все-таки сделал предрасчет состояния порта, что-бы в самом прерывании делать поменьше действий и меньше грузить камушек. Да и сам шим будет "аккуратней" выглядить.
void loop(){ .... setLevel(3,127); // ставим 50% яркость на 3-тьем канале ... } void setLevel(byte channelNo,byte channelLevel){ levels[channelNo]=channelLevel; // запоминаем, вдруг потребуется текущий уровень канала знать, если нет то нафиг levels и эту строчку. // делаем предрасчет состояния порта для каждого возможного pwn_cnt for(int i=0;i<=255;i++){ bitWrite(port_states[i],channelNo,i<channelLevel); } } void timer_action(){ PORTB=port_states[pwm_cnt++]; // тупо выставляем порт в следующие предрасчитанное значение }Если нам никогда не потребуется, где-то в логике кода, знать "какой сейчас level выставлен на канале", то массив levels - можно вообще выкинуть нафиг. Разм мы никогда его не читаем.
И еще... даже если мы пишем в порт напрямую, минуя digitalWrite, это не значит что мы обязаны выставлять режим порта напрямую. Можно выставить режим через pinMode, а писать через PORTB
Лично я - так предпочитаю. Открыл код - и сразу видно какие порты "на выход". Не нужно в Pin Mapping смотреть. Проще и безопастней подключать. Меньше шансов ошибится и включить на выход "не тот порт".
да знать какой шим в текущий момент не нужно. просто передаем значения какой нужно сделать
честно сказать не конца понял как работает. что за функция port_states и как работает?
насчет pinmode. да так проще, но в Pin Mapping все равно смотреть надо, чтобы знать каким портом и выводом управлять