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

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

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

Да никакая это не функция. Просто массив байтов. Нужные состояние PORTB для каждого значения pwm_cnt. Ну конечно объявить его вначале нужно.

byte port_states[TOTAL];

Ну смотрите. Что вы делаете при каждой сработке таймера?
1. Выясняете какие биты нужно установить в PORTB для текущего pwm_cnt. 
2. Устанавливаете эти биты.

Если, с предыдущего цикла pwm_cnt=0...255 ни один уровень канала не менялся, то пункт (1) будет выполнятся "вхолостую". Раз за разом делатся одна и та же работа.

Поэтому я предлагаю, пункт (1) делать только в момент когда какой-то канал поменял свой уровень. Делать это вне обработчика таймера. Что-бы не нагружать проц. лишней работой, не заставлять делать каждый раз сравниать pwn_cnt<level, раз результат это сравнения уже известен еще на момент выставления нового уровня.

То есть вместо:

>1. Выясняете какие биты нужно установить в PORTB для текущего pwm_cnt. 

Мы заранее(!), "выясняяем" какие биты должны быть установлены у PORTB. Делаем это для каждого возможного значения pwm_cnt. И сохраняем результат этого выяснения в port_states[]. А обработчик прерывания, уже только "берет готовенькое" из этого массива.

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

jeka_tm пишет:

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

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

Можно, конечно, в коментах написать "какие выходы используются". Только коменты это такое. Выходы в коде поменял, а коменты обновить забыл :(   Поэтому лично мне pinMode - она как-то "железобетонней". Ну вот тогда я ТОЧНО знаю какие пины у меня на выход и с кем нужно быть внимательным и аккуратным. Посмотреть глазами на макетку, перепроверить, прежде чем вливать скетч (если я какое-то время не работал с ним).

Но, конечно, это "дело вкуса" ;)

P.S. А когда берешь чужой код, с форума, то вдвойне хочется видеть в явном виде кто у нас на выход. Никому же нельзя верить ;)

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

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

так я не против pinMode, удобнее и проще для понимания.

просто все равно нужно было лезть Pin Mapping, чтобы знать что писать в bitWrite(), и заодно укоротил код в части настройки портов. изначально было так

for(byte i=8;i<=13;i++){
  pinMode(i, OUTPUT);  //настраиваем выводы с 8 по 13 на выход
}

верну к этому виду

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

>просто все равно нужно было лезть Pin Mapping, чтобы знать что писать в bitWrite()

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

Ищем в папке с аруиной "void digitalWrite" по файлам *.c

И находим файлик wiring_digital.c 

В котором:

void digitalWrite(uint8_t pin, uint8_t val)
{
	uint8_t timer = digitalPinToTimer(pin);
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *out;

	if (port == NOT_A_PIN) return;

	// If the pin that support PWM output, we need to turn it off
	// before doing a digital write.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);

	out = portOutputRegister(port);

	uint8_t oldSREG = SREG;
	cli();

	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

	SREG = oldSREG;
}

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

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

нифига не понял digitalWrite работает, кроме последней части и то неполностю

	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

тут передает 1 или 0 в бит. но сдвига нет

все дело в этих функциях. если правильно понял вы про них

digitalPinToTimer(pin);
digitalPinToBitMask(pin);
digitalPinToPort(pin);
 
 
leshak
Offline
Зарегистрирован: 29.09.2011

Я выделил строки жирным. Вам нужна только четвертая. По названию, если перевести дословно, название функции digitalPinToBitMask, то можно догадатся что она возвращает маску для соотвествующего пина. Делает то же самое, что вы глазами выясняли в гляди в Arduino - PinMapping168 . То есть возвращает "уже смещенное". Скажем для digitalPinoBitMask(13), вернет, так как это  PB5, B00010000

Ну а как маской устанавлитьвать биты (bitWrite нам тут уже не покатит, так как у нас у не номер, а маска) - вы сами процитировали кусок кода.

Только, если вы и так знаете что у вас PORTB, то можно вместо ссылки на порт (*out) писать, сразу PORTB&= и PORTB|=

Ну или тоже пользоватся ссылкой на порт. Которую получаете через digitalPinToPort

digitalPinToTimer - вам нафиг не нужен. Читайте же коментарии, смотрите для чего они выясняют таймер. Что-бы выключить хардварный PWM на этом пине, если он включен (это же универсальная функция). Если же вы знаете что вы там не включали, через analogWrite ничего, то на таймеры - можно забить.

digitalPinToPort - тоже не нужен. По коду видно что он используется только для того что-бы проверить "а вдруг нам дали номер пина который вообще не существует" - тогда вообще ничего не делать. Если номера пинов будете давать реальные, то  digitalPinToPort - тоже нафиг не нужен.

А можете просто забить на все это дело. Если не хотите делать "универсальное softPwm решение". Захардкодте и забудете.

Если же хотите разобратся, то напишите десяток тестовых скетчиков. С разными serial.print(,DEC), serial.print(,BIN). Подергайте эти функции, посмотрите что возвращают. Что делает с какой-либо переменной конструкция someVar|=mask, sameVae&=mask, someVar^=mask и т.д. Полазте по исходиникам, поищете как объявлены эти digitalPinoBitMask, там увидите использование загадочных digital_pin_to_port_PGM, задайтесь вопросом "а это что такое". Найдите где оно объявлено.... ну и узнаете где же, в итоге, хранится этот пин маппинг. Сложите в голове полную картину.

Узнаете как вообще можно переопределить номера пинов :) На некоторых дуинах скажем не все аналоговые ноги камня выведены на пины, если делаете "свою плату" их тоже можно заюзать. Только потребуются описать их, что-бы arduinoIDE узнала о их существовании (вообщем законфигурить своюй собственную плату).

Ну и доку читать, само собой Arduino - BitwiseAnd

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

подробно))

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

как раз код можно будет сделать универсальнее для жк дисплея1202 (дергаю напрямую портом, digitalWrite изза скорости не использую). ну это другая тема, а с пинами тоже самое

как раз сверху пример того как порт поменял, а комменты старые остались))

...
#define SC_On            PORTC |= 1<<4        // CS    PD5
#define Data_On          PORTC |= 1<<3        // Data  PD6
#define Clock_On         PORTC |= 1<<2        // Clock PD7
#define SC_Off           PORTC &= ~(1<<4)
#define Data_Off         PORTC &= ~(1<<3)
#define Clock_Off        PORTC &= ~(1<<2)

....

void SendByte(byte mode, byte c){
  SC_Off;
  (mode)? Data_On : Data_Off;
  Clock_On;
  Clock_Off;

  byte i;
  for(i=0;i<8;i++)
  {
    Clock_Off;
    if (c & 0x80) Data_On;
    else	  Data_Off;
    Clock_On;
    c <<= 1;
  }

  Clock_Off;
  SC_On;
  Data_Off;
}
...

вы зарабатываете этим? или тоже хобби? 

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

>вы зарабатываете этим? или тоже хобби? 

Сложный вопрос.

Вообще "основной заработок", это ServerSide программинг на C#, ну последнее время начал добавлятся и ClientSide на typescript/javascript.

А ардуино - вначале было хобби.

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

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

Так что пока вернулся к С#/javscript. Благо там есть "прокладка" в виде менеджера между мной и клиентом. Который очень многие вопросы берет на себя. Не зря кушает свой хлеб. А я занимаюсь то чем люблю - кодом.

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

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

понятно. значит вы программист

и "давно это разгрызли"

а я недавно начал заниматься, до этого больше в аналоговой или цифровой, но дискретной баловался

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

правда если вы например с украины, а я с россии какие проценты за это будут брать незнаю. знаю что в россии через сбербанк онлайн например с визы на визу без процентов. с визы на маэстро берут 1% вроде

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

jeka_tm пишет:

понятно. значит вы программист

и "давно это разгрызли"

Конкретные знания - они не так ценны как кажется. Важнее способность/методология их преобретать.

Когда я начинал ковырять дуину, для меня все эти "битовые сдвиги, регистры, указатели" - тоже были темный лес. С# имеет совсем другую идеологию, это на порядок более высокоуровневый язык, очень многое он "берет на себя" и программеру думать не нужно. Ну и, само собой, все эти аппаратные нюансы (ШИМ-ы, бутлоадеры, таймеры) - ничего этого не знал. 

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

И кстати если покопаетесь в форуме, то увидете что за пару лет разбирательства с железом, я задал/инициировал всегда "по железу" от двух до четырех вопросов :) Все остальное - хватало гугла ;)

>самый простой метод оплаты как мне кажется переводом на карту.

Ну видно что вы "теоретически". А я практически. Я же не говорил что "не знаю как". Сказал что "задолбало" и "раздражает" (поставил цель себе - я таки "проводил"). В россия/украина - без разницы. И там и там клиенты есть. Так что проблема "границы" в любом случае имеет место быть. А самые "вкусные клиенты" (и по деньгам и по приятности работы с ними), это Европа/США. А там все еще хуже с "переслать деньги". На порядок острее чувствуешь себя "из гондураса". Внутри - у них все просто. А вот "послать к нам" - там уже ихняя налоговая начинает пристально смотреть. Не говоря уже про то, что объяснить им забабахи нашей налоговой и банковской системы - та еще песня (для них даже понятие "мокрая печать" -это уже неведомая экзотика).

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

Ну понятно что "все это можно, все решаемо". Только на мелких проектах стоимость  администирования всего этого дела - довольна высока выходит. Так что, по крайней мере пока. Мне проще по web-бу. Через фирму. Где с клиентом работаешь по 5-8-мь лет. И суммы там совсем другие. Раз в пару месяцев достаточно "сделать одну/две проводки".

P.S. Да я знаю, что куча фрилансеров спокойно справляется с этими проблемами. Биржи есть и проч. посредники. Я попробова меня все это РАЗДРАЖАЕТ. Я люблю код :) Меня даже в клиент банке перебросить деньги с расчетного на карточный счет и то... пару дней нужно с духом собиратся ;)

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

как говорили: в институте не конкретные знания в основном приобретаются, а способность их искать и применять. тут я полностью согласен

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

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

попробовал. отлично работает. не сразу понял почему не работает, но потом осенило

#include <TimerOne.h>

#define TOTAL 6

byte port_states[256];
int brightness = 0;
int fadeAmount = 5;

volatile byte pwm_cnt=0;

void setup()  { 
  for(byte i=8;i<=13;i++){
  pinMode(i, OUTPUT);  //настраиваем выводы с 8 по 13 на выход
}

  Timer1.initialize(80);
  Timer1.attachInterrupt(timer_action);
} 

void loop(){
  
  for (byte i=0;i<TOTAL;i++){
    setLevel(i,brightness); // ставим 50% яркость на 3-тьем канале 
  }
  

  brightness = brightness + fadeAmount;
  if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ; 
  }     
  delay(200);   
}

void setLevel(byte channelNo,byte channelLevel){
  // делаем предрасчет состояния порта для каждого возможного pwn_cnt
  for(int i=0;i<=255;i++){
    bitWrite(port_states[i],channelNo,i<channelLevel);
  }

}

void timer_action(){
  PORTB = port_states[pwm_cnt++]; // тупо выставляем порт в следующие предрасчитанное значение
}

с digitalPinoBitMask(13) еще не разбирался

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

А если сделаете

 for (byte i=0;i<TOTAL;i++){
    byte lvl=brightness;
      lvl+=i*40;
    setLevel(i,lvl); 
  }

То должно получится что-то типа "волны" (не уверен правда, но "так чувствую").

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

попробую чуть позже. тут побаловался с digitalPinoBitMask(13)

вот что получилось, по сути урезал digitalWrite

void dWrite(byte pin, byte val){
  byte bit = digitalPinToBitMask(pin);
  volatile byte *out;
  out = portOutputRegister(digitalPinToPort(pin));

  if (val == LOW){
    *out &= ~bit;
  } 
  else {
    *out |= bit;
  }
}

сравнил по с digitalWrite, прямым управлением портом, и функцией bitWrite(); по скорости исполнения. и мне понравилось

digitalWrite                10,07 мкс

dWrite                         1,51 мкс

PORTC |= 1<<4;          1,13 мкс

bitWrite(PORTC, 4,0);  1,13 мкс 

проверял так. запомнил время. прогнал 30000 раз, вычел время, поделил на 300. получил время умноженное на 100 (для точности)

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

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

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

Хотя нет. Фигня получится... Нужно мудрить. Жаль хотелось малой кровью 

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

leshak пишет:

Хотя нет. Фигня получится... Нужно мудрить. Жаль хотелось малой кровью 

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

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

переделал либу под дисплей. пожирнее немного стала

но зато как просто настраивать

было. менять пины в ней для меня не самое любимое дело

#define SC_On            PORTC |= 1<<4      
#define Data_On          PORTC |= 1<<3       
#define Clock_On         PORTC |= 1<<2       
#define SC_Off           PORTC &= ~(1<<4)
#define Data_Off         PORTC &= ~(1<<3)
#define Clock_Off        PORTC &= ~(1<<2)

...

DDRC |= 0b00011100; 

...

стало. совсем другое дело

#define CS       5 
#define Data     6
#define Clock    7

спасибо Лешак. хорошую мысль подкинули

блин всего 3 цифры поменять. я столько раз перенастраивал...

мда... как говорил мой командир в армии:не доходит через голову, дойдет через руки и ноги (не лично мне)

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

jeka_tm пишет:

leshak пишет:

Хотя нет. Фигня получится... Нужно мудрить. Жаль хотелось малой кровью 

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

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

const float pi = 3.1415926535;
#define period 4000UL // 2 секунды на весь цикл
void loop(){
  unsigned long currentTimer=millis();
  for(byte i=0;i<TOTAL;i++){
      setLevel(i,127*(sin(2*pi*currentTimer/period+ (pi/3)*i )+1));
  }
}

 

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

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

...

что то не то. плавно загораются слева направо, но быстро, и потом постоянно горят

я даже уже не пытаюсь понять код, голова не работает, спать пора

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

#include <TimerOne.h>

#define TOTAL 6
#define period 4000UL // 2 секунды на весь цикл

byte port_states[256];
int brightness = 0;
int fadeAmount = 5;

const float pi = 3.1415926535;
volatile byte pwm_cnt=0;

void setup()  { 
  for(byte i=8;i<=13;i++){
    pinMode(i, OUTPUT);  //настраиваем выводы с 8 по 13 на выход
  }

  Timer1.initialize(10);
  Timer1.attachInterrupt(timer_action);
} 

void loop(){
  unsigned long currentTimer=millis();
    for(byte i=0;i<TOTAL;i++){
      setLevel(i,127*(sin(2*pi*currentTimer/period+ (pi/3)*i )+1));
    }
  //delay(200);   
}

void setLevel(byte channelNo,byte channelLevel){
  // делаем предрасчет состояния порта для каждого возможного pwn_cnt
  for(int i=0;i<=255;i++){
    bitWrite(port_states[i],channelNo,i<channelLevel);
  }

}

void timer_action(){
  PORTB = port_states[pwm_cnt++]; // тупо выставляем порт в следующие предрасчитанное значение
}



 

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

jeka_tm пишет:

что то не то. плавно загораются слева направо, но быстро, и потом постоянно горят

100% что-то не то. Залил ВАШ код из №69 в нану. Правда у меня светики подключены на D9 (внешний) и D13 (встроенный) . Так что "волну" я увидеть в любом случае не могу, но каждый из разгорается-тухнет независимо. Никакого "и потом постоянно горят" - явно не наблюдается. Так что похоже что "влили что-то не то" (не то что привели в сообщении выше).

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

хорошо вечером еще раз проверю

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

Ну не может оно "загорется и гореть". Вот графики яркости для первых двух каналов:

https://www.wolframalpha.com/share/clip?f=d41d8cd98f00b204e9800998ecf842...

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

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