Игрушка для ребенка. Кубик

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

Решил сделать игрушку ребенку. 

Куб. 6 сторон соответственно. в каждой по 4 светодиода, подключенных через полевой транзистор

также ик диод и ик фототранзистор в качестве датчика

смысл следующий. обрабатываем датчики. и при определенных условиях включаем нужную сторону или по кругу зажигаем светодиоды

если хватит места еще плата с mp3 плеером чтобы звуки воспроизводить

итого 24 светодиода, 6 датчиков. для каждой стороны своя плата. + плата управления+преобразователь+ литиевый аккумулятор =  8 плат. если плерр влезет получится 9 плат

предлагаю присоеденится по желанию. необходима помощь в алгоритмах. ведь будет срабатывание и на поверхность на которой лежит и на руки и тп. датчику пофигу

буду рад любым предложениям

ну и фото. вырезали сегодня на станке

изнутри покрашу черным

Клапауций
Offline
Зарегистрирован: 10.02.2013

я бы акселерометры установил по трём плоскостям, а по сторонам по одному RGB светодиоду - достаточно будет для индикации оттенков цвета и реакции сторон на повороты в пространстве, встряхивания, жесты...

*24 светодиода - недоерунда получится, да и батарею сожрёт быстро.

com
Offline
Зарегистрирован: 06.09.2013

чего-то вспомнилось  "а хотите я его стукну? он станет фиолетовый в крапинку!"

а вообще идея интересная. насчет быстро батарею сожрет... на пару часов в любом случае хватит, а этого наиграться достаточно. только надо разъем для зарядки вывести на корпус, чтобы не разбирать.

и по поводу корпуса - а чего не вырезали сразу из непрозрачного пластика, цветного какого-нибудь, чтобы не красить изнутри?

 

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

красить нужно было чтобы свет одной стороны не светил на другие

да и непрозрачного пластика не было приемлемой толщины, а искать не хотелось. сделал из того что было

я покрасил. свет не распространяется равномерно, чтобы равномерно надо с торца засвечивать

rgb светодиодов под рукой не было, поставил зеленые

акселерометр есть mpu6050? может приделаю

проверил датчики приближения. реагируют где то сантиметров с 5. до этого только вплотную реагировали, пришлось увеличить ток ик диодов

насчет батареи: постоянно светятся только ик диоды, остальные будут включаться по условиям, да и то не все

плату зарядки тоже засуну если влезет

 

com
Offline
Зарегистрирован: 06.09.2013

какого размера кубик, а то по фото трудно оценить?

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

забыл написать 

внутренний размер 50х50х50мм

наружний 58х58х58мм

вопрос. кто тестировал либу SoftPWM. там нет каких нибудь глюков или ограничений?

нужно 6 каналов шима просто, а в меге8 только 3 хардварных

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

вопрос. кто тестировал либу SoftPWM. там нет каких нибудь глюков или ограничений?

Не тестировал, но "а шо ей станется?". Даже если есть подглюки с таймингами, то учитывая вашу задачу заметить их "на глаз" - никаких шансов. Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.

А еще, учитывая размеры куба (место не нужно жестко экономить) можно посмотреть на что-то типа TLC5940

Драйвер светодиодов. 16-ти канальный. С PWM. Поддерживает каскадирование  (можно взять несколько штук и подключить их одну за другой, не увеличивая количество занятых ног камня). Умеет сам выдерживать стабильный ток на светодиод (от 0 до 120ma задать можно) - не нужно каждому светику свой резистор лепить, имеет Dot Correction (правда у меня пока не заработал толком, но не больно и нужно в данный момент :), контроль по температуре и обрыву светодиодов.

Вообщаем так сказать "запас на будущие" (вдруг после сборки захочется побольше светодиодов, или те 6-ть которые есть, захочется сделать RGB что-бы куб переливался).

Единсвенное что - библиотека его. "Под капотом", по крайней мере мне, она ОЧЕНЬ не нравится. То ли автор настолько крут что я его не понимаю, но IMHO наверченно много лишнего и конфигурение сделано через ж....

Из серьезных минусов - библиотека забирает "под себя" два таймера (IMHO можно было-бы и одним обойтись).

ну и Dot Correction, по крайней мере у меня на MEGA работать не захотел. На более младших камнях - пока не пробовал.

P.S. Dot Correction - это компенсация разницы яркости светодиодов. К примеру синие светят ярче чем красные, и не хочется каждый раз учитывать это в коде. Можно зашить в память микрухи  "поправку на ветер", и в коде считать их "одинаковыми".

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет

задачу с токами я упростил. падение напряжения на зеленом светодиоде (которые у меня валяются) около 2,5В. и я просто поставил 2 последователь. проверил ток. все норм даже если просто 5В подать.

поставиk полевик irlml2502. пока все норм. надо вставлять платы, склеивать корпус, делать плату управления и запихивать все внутрь. платы для каждой стороны уже собраны

кстати пока баловался с платой (тестил токи, датчики) если затвор полевика не подтянуть на землю реагирует на прикосновение пальца. только как тиристор включился и не выключается. Но хотя бы выключается пока не тем же пальцем не замкнешь на землю. вот думаю сенсор попробовать сделать отдельно. получится ли добится стабильной работы. если да то пригодится

leshak
Offline
Зарегистрирован: 29.09.2011

И еще, а зачем мучать себя "как при царском режиме"? Это я про Atmega8 в качестве целевого.

Почему не Atmega328?  Оно очень неприятно, под конец проекта уперется в "ой памяти не хватает, что делать?"

Если планируется крупная серия девайсов - тогда понятно желание экономить. А на одной штучке - никакого резона.

А нас  328 стоит на $0.7 дороже восмерки. Бывают периоды когда 328 даже дешевле  оказываются.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

328 в наличии только в платах ардуины. игрушка будет одна для ребенка, никакой партии

мне кажется что памяти хватит и много останется. нечему там особо память кушать. сложных алгоритмов не предполагается

половина наверно будет пустая. но если реально не хватит ардуину внутрь засуну. обещаю

вот думаю над алгоритмами. думаю пока так

1. 1 приближение и убрали сторона загорелась и выключилась (плавно)

2. два быстрых как бы хлопка по стороне. стороны вокруг этой стороны "крутятся" и музыка включилась

надо может еще что нибудь. хотя наверно это уже в собранном виде при игре будет понятнее

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет

Да. Наличие это для них дейсвительно проблема. Зато в остальном... только на полевиках сколько места можно съкономить.

И резисторы токоограничивающие не нужны (да я видел что вы решили проблему "последовательностью включения", но лично мне такое решение не очень нарвится :)

 

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

мне кажется что памяти хватит и много останется. нечему там особо память кушать. сложных алгоритмов не предполагается

Ну хозяин барин. А память кончается всегда "неожиданно", причем проявляется это совсем приятно. Никаких ошибок "память кончилась" - тут не предвидится. Просто "начинает глючить" (перегружается, бред в переменных) и вы недельку тратите что-бы понять в чем проблема (вначале-то думаете что проблема у вас в коде или библиотеки глючные). Проблема с нехваткой места которая обнаруживается при заливке (скетч не влазит) - это если повезет. Нехватка RAM - вот чего боятся нужно.

А выжрать память и без алгоритмов хитрых - "как два пальца обо...".

Подключил парочку библиотек, натыкал Serial.println для дебага, объявил какой-нибудь массивчик типа long побольше размером и..... готово :)

Вообщем мне проще переплатить $0.7 и чувствать себя более спокойно :)

ales2k
Offline
Зарегистрирован: 25.02.2013
freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Во всех скетчах использую, чтобы без неожиданностей - возвращает количество свободной оперативы

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

leshak пишет:

jeka_tm пишет:

хорошо. буду иметь в виду на будущее. но сейчас врядли. в наличии нет

Да. Наличие это для них дейсвительно проблема. Зато в остальном... только на полевиках сколько места можно съкономить.

И резисторы токоограничивающие не нужны (да я видел что вы решили проблему "последовательностью включения", но лично мне такое решение не очень нарвится :)

 

как любил говорить мой начальник дешево и сердито. это я про последовательное включение. так у меня есть max7219. но разводка с ней в корпусе уж больно жесткая будет изза матричного подключения светодиодов

а насчет полевиков тут не согласен. SOT23 очень маленький корпус, даже 6 таких корпусов будут скорее всего меньше

лешак. как только будет мало места сразу вставлю плату ардуины (хоть и не хочется) ну нет у меня в наличии 328 меги отдельной. в кварц поблизости можно было бы сгонять, но они даже за мегу 88 в дип корпусе 600 рублей хотят. совсем обнаглели

ales2k пишет:

freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Во всех скетчах использую, чтобы без неожиданностей - возвращает количество свободной оперативы

 

спасибо за код. я раньше его юзал когда массивы для символов жк дисплея в опертивке хранил. вот я намучался с нехваткой оперативки. буду навсякий случай проверять

com
Offline
Зарегистрирован: 06.09.2013

jeka_tm пишет:

вот думаю над алгоритмами. думаю пока так

1. 1 приближение и убрали сторона загорелась и выключилась (плавно)

2. два быстрых как бы хлопка по стороне. стороны вокруг этой стороны "крутятся" и музыка включилась

надо может еще что нибудь. хотя наверно это уже в собранном виде при игре будет понятнее

загорается произвольная сторона, нужно повернуть горящими диодами вверх за 0.хх секунд и поставить на стол, успел - веселая музыка, не успел - грустная, и так много раз подряд.

зы. если договориться, что переворачивать кубик можно только носом, то получится неплохой аттракцион для подвыпившей компании взрослых :)

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

)))) прикольно. надо будет сделать

еще надо будет попробовать звук генерить, на меге8 наверно не получится. таймеров не хватит, но вообще интересно

имперский марш))

http://proapi.ru/melodiya-na-arduino-uno-r3/

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

 но они даже за мегу 88 в дип корпусе 600 рублей хотят. совсем обнаглели

Ох нифига себе... У нее что выводы платиновые? У нас за эти деньги можно  Nano(клон) или Uno(клон), или Mini оригинал, или  две Pro Mini (клон).  

Ну вообщем понятно, вам лучше исходить из того что есть в наличии :)

Просто при следующих закупках стоит подумать "что лучше брать.."

Я, в свое время, тоже закупился Atmega8, про запас, и только много позже понял что это был "не самый оптимальный выбор".

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

при следующей закупке куплю 328. бывает острая необходимость

да у нас тоже можно купить. в митино все гораздо дешевле, но туда долго добираться. полтора часа в одну сторону

+ видно цены немного упали. а меги 88 вообще нет

328 вообще нет.

мега32а смд стоит 150 рублей (самое дешевое что есть)

а мега8а дип 280 рублей. где логика

мега16 в дип 900 рублей)))

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

блин вот я бился бился с звуковым модулем, а потом почитал даташит. он гигабайтные флешки максимум поддерживает

я про WTV020-SD. блин и как назло минимум флешки 2гб. облом звука не будет

+ уменьшил размер  раздела до 900. сконвертировал в wav 12кгц, название песни по английски на всякий. не помогло к сожалению

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

все ставлю arduino micro в игрушку пока не придут 328 меги. нужно 6 шимов. хотел сделать софварный на меге8, но там таймеров не хватает. статьи встречаются про софтварный шим на меге8, но либо написаны на асме, либо на баскоме, либо вообще что то не вменяемое. нашел приличное видео чувак сделал софтварный шим на 12 каналов вроде, но без ссылки на источник

блин на тиньке 13 делают софтварный шим, а тут на меге проблема с исходниками

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

 Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

может я что то путаю. давненько читал. надо перечитать. но вроде не получится на меге8 его использовать. наверно путаю

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

лашак подскажите пожалуйста. библиотека timerone работает на 1 таймере. этот таймер есть в меге8. но компилятор ругается на пример из библиотеки. сам не понял что нужно исправлять

C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp: In member function 'void TimerOne::attachInterrupt(void (*)(), long int)':
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp:117: error: 'TIMSK1' was not declared in this scope
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp: In member function 'void TimerOne::detachInterrupt()':
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp:125: error: 'TIMSK1' was not declared in this scope
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp: In member function 'void TimerOne::start()':
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp:143: error: 'TIMSK1' was not declared in this scope
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp:144: error: 'GTCCR' was not declared in this scope
C:\YandexDisk\arduino-1.0.4\libraries\TimerOne\TimerOne.cpp:144: error: 'PSRSYNC' was not declared in this scope

 

leshak
Offline
Зарегистрирован: 29.09.2011

Попробуйте, в файлик TimerOne.cpp 

После строчки

Между строками

#include "TimerOne.h"

[СЮДА ВСТАВИТЬ]

TimerOne Timer1;  

Воткнуть вот это:

#if defined(__AVR_ATmega8__)
#define TIMSK1 TIMSK
#define GTCCR SFIOR
#define PSRSYNC PSR10
#endif 

P.S. Похоже, таки авторы библиотеки не сделали поддержку ATMEGA8 :(  Этот кусок первое что "нагуглилось". Особо не вникал в него. Если он не поможет (компилироваться будет, а вот работает ли - не на чем проверить), тогда таки прийдется раскуривать даташит и руками самому конфигурить таймер, без библиотеки..

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

скомпилировалось. работает или нет дома уже проверять. спасибо. я много страниц пересмотрел. не попадалось такого вроде

проглядел наверно

leshak
Offline
Зарегистрирован: 29.09.2011

В принципе более оптимальным (что-бы самому заново не реализовывать) было бы "дофиксать", саму 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 . Вообщем просто нужно сесть и "внимательно вкурить". Но на это сейчас, к нема времени. А готовый кусок, к сожалению, нагуглить не вышло.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

я пытался разобраться. но пока эти таймеры для меня темный лес

читаю даташит но пока не понял почему например регистр SFIOR

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

блин часа два уже точно ломаю голову. предложенная вставка работает, но только на выводах с хардварным шимом. на других нет

как говорится печалька

и столько "редисок" на ютубе. видео выкладывают без ссылки на источник. там и 12 канальный шим делали

leshak
Offline
Зарегистрирован: 29.09.2011

>блин часа два уже точно ломаю голову. предложенная вставка работает,

Какая предложенная вставка? Для TimerOne? Так а потом как вы pwm делаете? Через Timer1.pwm? или через Timer1.attachInterrupt()?

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

через Timer1.pwm

вставка эта

#if defined(__AVR_ATmega8__)
#define TIMSK1 TIMSK
#define GTCCR SFIOR
#define PSRSYNC PSR10
#endif 

 

leshak
Offline
Зарегистрирован: 29.09.2011

leshak пишет:

leshak пишет:

 Даже если "вообще не работает", но то ничто не мешает взять библиотеку, скажем TimerOne.attachInterrupt и самому дергать ноги.

leshak
Offline
Зарегистрирован: 29.09.2011

"самому дергать ноги" - это значит самому реализовать програмный шим. А 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

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

не особо понял

предполагаю так

#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++;
}

больше всего непонятна эта запись

for(byte i=0;i<TOTAL;i))digitalWrite(pins[i], pwm_cnt<levels[i]);

наверно что то вроде этого

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++;
}

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

переделал по управление портами. вроде должно быть правильно. что скажете Лешак

до меня кажется дошло почему просто использовать прерывание и ногодрыганьем делать шим

#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++;
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

for(byte i=0;i<TOTAL;i){ //i=0, и пока ш меньше TOTAL, i. что делает просто i? обычно же i++ или i--

Конечно. Вы правы. Должно быть i++. Очепятка.

jeka_tm пишет:

спасибо Лешак. долго гуглил, но инфы нормальной так и не нашел

А кто вам сказал, что для любой проблемы есть ответ в гугле? ;) К сожалению, система образования приучает нас к этому. Есть в учебник (или гугл), в котором есть ответы на все типовые вопросы. Нужно их только "заучить" и получить максимальный бал на экзамене. А когда, потом, человек приходит на работу - оказывается что нужно как раз то за что "выгоняли с экзамена": пользуйся любой литературой, общайся с кем хочешь.... но для самых интерестных-оплачиваемых задач - ответа готового вообще нет. Его нужно придумать/найти. И в этом же самый кайф ;)  Вот раскрой своего кубика, вы в гугле искали или почесали в затылке, прикинули так/этак и нарисовали?

Кстати... ответ в гугле - таки есть. Выше - я упоминал про микруху TLC5940. Если бы из любопыства почитали что это за зверь (только детально, а не "берем микруху, берем готовую либу... профит"), то увидили бы, что я просто програмно реализовал програмно то что делает эта микруха. А сама "идея" описана в даташите. 

P.S. Это я не на вас бурчал, а на "систему образования в общем..." ;)  Вы, в принципе, все делаете правильно. По крайней мере с моей точки зрения :)  А она - тоже не истина в последней инстанции ;)

leshak
Offline
Зарегистрирован: 29.09.2011

>вроде должно быть правильно. что скажете Лешак

А зачем меня спрашивать? Спросите ардуину. Или в думаете у меня в голове есть Avr-эмулятор?  Есть конечно, но качество в любом случае похухе чем у настоящего :)

>Timer1.initialize(1000);

Я же говорил: "берите поменьше".

Ну давайте прикинем что у вас получилось. "Счетчик" будет тикать один раз в миллисекунду. Тикает он от 0 до 255. Значит "один оборот" происходит за 255ms. То есть, диод будет делать вкл/выкл 1000/255 ~=4 раза в секунду. Думаю такое мерцание на глаз не заметить может только слепой.  У вас получился ШИМ с частотой ~4 герца. Дефолтный апаратный ШИМ, если память не изменяет, имеет частоту 497 Hz или 1 kHz (в зависимости от пина). Почувствуйте разницу.

leshak
Offline
Зарегистрирован: 29.09.2011
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, для каждого канала, на каждую сработку таймера.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

ну раскрой придумать гораздо проще чем программный шим. лично для меня

как только не тестил. была фигня. решил что сразу много пинов это слишком. надо начать с одного

#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. тормоз одно слово. при большом увеличении ловит импульс, точнее начало импульса. завтра на работу возьму. будет проще разобраться. и есть ли там вообще шим

leshak
Offline
Зарегистрирован: 29.09.2011

>ну раскрой придумать гораздо 

Да ничего подобного. Раскрой что-то не додумал - расход материала и времени. А програмно - пробуй сколько хочешь. Попробовал так, попробовал этак.... Вообщем тут вопрос только "насколько часто вы это делаете".

Кстати один из способов прокачать этот навык "помощь на форуме" :)  

>тусклоо горит.

Тут больше похоже что с железом. Судя по вашему коду - оно вообще должно гореть почти постоянно. А если просто на этой ноге включить светик, без всяких шимов. Он вообще горит?

И зачем два if-а?  Вариант с else - был вполне себе нормальным.

>без осциллографа не разобраться. подцепился DSO nano

Погуглите есть OpenSource клиенты логического анализатора. И соотвествующий скетч для ардуины. Гуглите "arduino logic analyzer sump"

>будет проще разобраться.

Щас попробую своим глянуть, что оно у вас дает...

Но естетсвенно будет давать  "не то". Так как вы послушали "Я же говорил: "берите поменьше". И сделали в точности наоборот.

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ой.ой... а зачем же вы в timer_action запихнули for, раз у вас остался один канал? Ну кончно работать не будет. Вы же на каждую сработку таймера теперь пробегаете сразу все значения pwm_cnt, естественно фигня выходит.

Если вы хотели "оставить один канал", то вам нужно было взять #32, переименовать levels в level (ну и естественно не массивом), а сам for просто выбросить из обработчика. for был нужен только что-бы "пробежатся по всем каналам". Если канал остался один - то собственно и for не нужен.

leshak
Offline
Зарегистрирован: 29.09.2011

Ну вообщем взял базовый пример:

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++;

}

 

leshak
Offline
Зарегистрирован: 29.09.2011

>а анализатор подключать - меня все-таки обломало

Или не обламало. Вот при .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);
}

Это я к тому, что "нигде не описанно" ;)

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

спасибо. разжевали

>А если просто на этой ноге включить светик, без всяких шимов

да работает. и светодиод на всякий случай постоянно проверял

>И зачем два if-а?  Вариант с else - был вполне себе нормальным.

уже начал сомневаться, сделал на всякий случай. до этого был else

>Погуглите есть OpenSource клиенты логического анализатора. И соотвествующий скетч для ардуины. Гуглите "arduino logic analyzer sump"

хорошо почитаю

>Но естетсвенно будет давать  "не то". Так как вы послушали "Я же говорил: "берите поменьше". И сделали в точности наоборот.

ставил и больше и меньше. но 10 не пробовал ставить

>Ой.ой... а зачем же вы в timer_action запихнули for, раз у вас остался один канал?

я предполагал работает так.

через определенные промежутки времени запускаем подпрограмму. промежутки времени соответствууют периоду колебаний. в подпрограмме делаем цикл до максимального значения шим. сравниваем необходимое значение счетчика с необходимым значением шим, пока меньше на выходе "1", стало больше на выходе "0"

>Уменьшил до Timer1.initialize(10); - ну вообщем на глаз четко видно что PWM

попробую что у меня получится

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

работает)))) спасибо еще раз

понял в чем моя ошибка. при прерывании запускается подпрограмма. в подпрограмме получился как бы счетчик 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() я не знал. теперь буду знать

уже поздно. завтра попробую сразу на нескольких пинах сделать

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

. в подпрограмме делаем цикл до максимального значения шим. 

Зачем? У нас же цикл сразу "Раззззз и прокрутился весь". А нам нужно что-бы он крутился с заданной скоростью. Сам вызов обработчика прерывания по таймеру  это уже "как-бы цикл". Только тело цикла  вызывается с некоторой переодичностью. 

Цикл нам нужен только если каналов много. Что-бы пробежатся по каждому каналу выставить ногу в нужное положение. Но опять-таки в этом цикле, которым мы бежим по каналам, сам счетчик 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++;
}

Вообщем было-бы желание, даже с голой ардуиной без внешнего железа и измерительных можно со многим разобратся ;)

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

да я уже понял свою ошибку. написал уже в предыдущем сообщении

кстати раньше не знал что в значения передаваемые можно ставить условия. теперь узнал что если условие выполняется результат равен 1, если нет 0

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

проверил с 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 минус сам отсекается

ну все что нужно было сделано

делаю плату управления, собираю кубик

 

leshak
Offline
Зарегистрирован: 29.09.2011

jeka_tm пишет:

ну все что нужно было сделано

Вот поэтому я постоянно и твержу: перед тем как братся за "супер-проекты" нужно построчно вкиснуть в базовые примеры. Прямо в ArduinoIDE, делаем File/Examples/Basics/Fade

И другие примеры тоже посмотреть стоит. Не сейчас, так потом пригодится. Идеи/подходы.

jeka_tm пишет:

проверил с 6 каналами. работает отлично))

1. Что-бы "работало долго и счастливо" стоит еще открыть даташит на камень и найти какой максимальный ток можно брать с одного пина и СУММАРНО с пинов подключеных к одному порту (естьи  еще и третье ограничение - со всех пинов вместе взятых). И убедится что вы их не превышаете.

2. Если этот код выше "только проверить", то все нормально. Если так и будет "в итоге", то не понятно. Зачем вам массив channels[] если у него всегда все элементы имеют одинаковое значение. Можно и одной переменной level обойтись. После чего возникнет следующий вопрос: если у нас все светики "работают синхронно", то на кой кляп нам вообще нужно занимать столько ног, городить програмный шим и т.п. Хватит одной ноги и плебейского analogWrite на ногу с аппаратным шимом.

jeka_tm пишет:

Лешак подскажите пожалуйста правильно ли я понял

Не нужно спрашивать. Натыкайте Serial.println-нов, выведите переменный в Serial и сами посмотрите что с ними происходит.

jeka_tm пишет:

2. при крайних значениях яркости меняем знак у яркости. но так как в шим заложен тип данных byte минус сам отсекается

Нет. Если мы в одном месте использовали переполнение, это не значит что нужно видеть переполнение везде и всюду.

> меняем знак у яркости.

Возмите словарик и переведите слова brightness, fade, amount. Посмотрите внимательно у чего именно мы меняем знак. А еще лучше откройте оригинальный fade-пример (через ардуиноIDE или по ссылке которую я вам давал). И почитайте коментарии к каждой строке (там они более подробны, я их потер для экономии экранного-места).

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

ток не превысит. на каждой стороне свой полевик уже стоит, платы для сторон собраны.

нужно было только считывание с датчиков, это не проблема. ну и управление с шимом. теперь это тоже сделано

каналов нужно именно 6. каждая сторона будет светится независимо, просто во время тестов сделал все одинаковым

проект несложный. меги328 просто в наличии не было. уже заказаны. покинули почту китая

усложнилось из-за программного шима, так как в меге8 не хватает аппаратных шимов

я уже собирался туда ардуину вставлять на время, пока 328 не придут

но вы помогли и ставлю мегу8. потом все равно заменю на 328, нужно будет еще кое что добавить, но на меге8 это уже не вариант делать

а насчет почитать. да надо читать. знаний не хватает. я обычно для этого просто делал 2 цикла "вверх" и "вниз"

но ваше решение компактнее. прочитал еще раз и понял как он работает. как все просто

leshak
Offline
Зарегистрирован: 29.09.2011

Ну если нужно все-таки отдельно, то я бы на вашем месте, все-таки сделал предрасчет состояния порта, что-бы в самом прерывании делать поменьше действий и меньше грузить камушек. Да и сам шим будет "аккуратней" выглядить.

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 смотреть. Проще и безопастней подключать. Меньше шансов ошибится и включить на выход "не тот порт".

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

да знать какой шим в текущий момент не нужно. просто передаем значения какой нужно сделать

честно сказать не конца понял как работает. что за функция port_states и как работает?

насчет pinmode. да так проще, но в Pin Mapping все равно смотреть надо, чтобы знать каким портом и выводом управлять