Можно ли собрать байт "за такт"

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

Есть способ собрать байт с 8 цифровых входов контроллера за 1 ход? Или это аппаратно не заложено?

alex_r61
Offline
Зарегистрирован: 20.06.2012
Например in r16, PIND, если с одного порта.
strarbit
Offline
Зарегистрирован: 12.06.2016

Если байт – это логические уровни на 8-ми контактах мк, то можно

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

strarbit пишет:

Если байт – это логические уровни на 8-ми контактах мк, то можно

как?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

пост №2

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

То есть я для этого должен на PD усы положить по биту согласно распиновке, а PIND в коде будет как готовый байт согласно этих данных в любой момент времени?

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

LastHopeMan пишет:

То есть я для этого должен на PD усы положить по биту согласно распиновке, а PIND в коде будет как готовый байт согласно этих данных в любой момент времени?

а, зачем ты спрашиваешь, если можно взять и проверить - здесь и сейчас?

*очевидно же - если можно записать в порт одной командой что-то, то очевидно можно и считать порт в переменную за один раз.

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

И записать можно одной командой весь байт?

Yarik.Yar
Offline
Зарегистрирован: 07.09.2014

Знаете, есть такая вещь, даташит называется....

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

LastHopeMan, Вы никогда не интересовались, временем выполнения команд digitalWrite и digitelRead?

Поинтересуйтесь - время их выполнения составляет 5-7 мкс т.е. порядка 100 тактов. Как Вы думаете, чем микроконтроллер занят все это время? Именно выколупыванием из байта, считанного из регистра, нужного бита (или чтением из регистра, установкой нужного бита в байте и запихиванием результата обратно в регистр).

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

andriano пишет:

LastHopeMan, Вы никогда не интересовались, временем выполнения команд digitalWrite и digitelRead?

Поинтересуйтесь - время их выполнения составляет 5-7 мкс т.е. порядка 100 тактов. Как Вы думаете, чем микроконтроллер занят все это время? Именно выколупыванием из байта, считанного из регистра, нужного бита (или чтением из регистра, установкой нужного бита в байте и запихиванием результата обратно в регистр).

рептилоид, к чему ты это всё рассказал? - ему нужно:

LastHopeMan пишет:

собрать байт с 8 цифровых входов контроллера за 1 ход

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

andriano пишет:

LastHopeMan, Вы никогда не интересовались, временем выполнения команд digitalWrite и digitelRead?

Поинтересуйтесь - время их выполнения составляет 5-7 мкс т.е. порядка 100 тактов. Как Вы думаете, чем микроконтроллер занят все это время? Именно выколупыванием из байта, считанного из регистра, нужного бита (или чтением из регистра, установкой нужного бита в байте и запихиванием результата обратно в регистр).

Это был мой следующий вопрос. Спасибо.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну .. Вы слишком пессимистчны. Заколупыванием и выколупыванием оно там занимается гораздо меньше времени. Большую часть оно занимается "защитой от дурака" и даже поболе чем 5-7мксек., вроде как.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

LastHopeMan пишет:

И записать можно одной командой весь байт?

Вы когда-то говорили, что Вы программист. Позвольте мне в этом усомниться.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

LastHopeMan пишет:

И записать можно одной командой весь байт?

Вы когда-то говорили, что Вы программист. Позвольте мне в этом усомниться.

Имею мнение в пользу переименовании персонажа из LastHopeMan -> HopelessMan.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Вот код digitalWrite() из одной из последних IDE.

Я хочу спросить, где <sensored> "защита от дурака". на <sensored> мкс.

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

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

 

Защита тут не от "дурака", а просто выключение ШИМа, если он включен.

А время тратится ТОЛЬКО на то, чтобы можно было использовать переменные в номерах пинов. Вот три первые строки и обращаются к макросам, переводящим числа в адреса портов и пинов, на основании таблиц.

Arhat109-2: Раз уж мне дали права .. ниже дано пояснение в чем абзац выше неверен

Вот именно это съедает время, но делает обращение к GPIO универсальным и платформо-независимым. То есть ИМЕННО это - основная фишка IDE. Для этого она  (среда) и придумана.

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

ну .. где-то тут на мой скромный взгляд:

static void turnOffPWM(uint8_t timer)
{
	switch (timer)
	{
		#if defined(TCCR1A) && defined(COM1A1)
		case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
		#endif
		#if defined(TCCR1A) && defined(COM1B1)
		case TIMER1B:   cbi(TCCR1A, COM1B1);    break;
		#endif
		#if defined(TCCR1A) && defined(COM1C1)
		case TIMER1C:   cbi(TCCR1A, COM1C1);    break;
		#endif
		
		#if defined(TCCR2) && defined(COM21)
		case  TIMER2:   cbi(TCCR2, COM21);      break;
		#endif
		
		#if defined(TCCR0A) && defined(COM0A1)
		case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
		#endif
		
		#if defined(TIMER0B) && defined(COM0B1)
		case  TIMER0B:  cbi(TCCR0A, COM0B1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2A1)
		case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2B1)
		case  TIMER2B:  cbi(TCCR2A, COM2B1);    break;
		#endif
		
		#if defined(TCCR3A) && defined(COM3A1)
		case  TIMER3A:  cbi(TCCR3A, COM3A1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3B1)
		case  TIMER3B:  cbi(TCCR3A, COM3B1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3C1)
		case  TIMER3C:  cbi(TCCR3A, COM3C1);    break;
		#endif

		#if defined(TCCR4A) && defined(COM4A1)
		case  TIMER4A:  cbi(TCCR4A, COM4A1);    break;
		#endif					
		#if defined(TCCR4A) && defined(COM4B1)
		case  TIMER4B:  cbi(TCCR4A, COM4B1);    break;
		#endif
		#if defined(TCCR4A) && defined(COM4C1)
		case  TIMER4C:  cbi(TCCR4A, COM4C1);    break;
		#endif			
		#if defined(TCCR4C) && defined(COM4D1)
		case TIMER4D:	cbi(TCCR4C, COM4D1);	break;
		#endif			
			
		#if defined(TCCR5A)
		case  TIMER5A:  cbi(TCCR5A, COM5A1);    break;
		case  TIMER5B:  cbi(TCCR5A, COM5B1);    break;
		case  TIMER5C:  cbi(TCCR5A, COM5C1);    break;
		#endif
	}
}

ежели вчё, оператор switch() компилятыр приобразуит в серию последовательных проверок .. :)

P.S. утверждение оказалось неверным. Этот switch() разворачивается в циклическую проверку. Смотреть - ниже.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Ну вот это например :

if (port == NOT_A_PIN) return;

if (timer != NOT_ON_TIMER) turnOffPWM(timer);

uint8_t oldSREG = SREG;

cli();

...

SREG = oldSREG;

Явно для защиты от дурака, ни для чего другого оно не служит.<sensored>

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

<sensored>

Там ещё кузявее. Мало того, что массивы перекодировки занимают память (для меги так 70 пинов определяется), вот это:

#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )

Как раз НЕ содержит "защиты от дурака" - выход за границу массива.. :)

А ещё разворачивается в "ближнее чтение", что для большой программы для Мега2560 тупо даст ошибку выполнения. Крайне трудно обнаружимую..

 

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

<sensored>

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

wdrakula пишет:

А время тратится ТОЛЬКО на то, чтобы можно было использовать переменные в номерах пинов. Вот три первые строки и обращаются к макросам, переводящим числа в адреса портов и пинов, на основании таблиц.

Вот именно это съедает время, но делает обращение к GPIO универсальным и платформо-независимым. То есть ИМЕННО это - основная фишка IDE. Для этого она  (среда) и придумана.

 

Такие вещи делаются одним обращением к массиву по индексу, где здесь сложные алгоритмы на несколько микросекунд?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Пояснение:

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

То, что Вы процитировали - в целом вовсе и НЕВЕРНО, ибо смотрим в код:

Собственно "записью" в порт занимаются строки 20 и 22, каждкая из которых транслируется в 3(три) команды МК: "считать порт", "логически домножить или сложить" и "записать в порт".

Строки с 16 по 25 - тупо обеспечение "консистентности" операции и выбор что пишем 0 или 1. Сама по себе консистентность тут никуда и не уперлась, это такая "перестраховка дурака", то бишь избыточность.

А вот теперь ВНИМАТЕЛЬНО смотрим на строку 14, которую автор вашей цитаты ДАЖЕ НЕ УПОМЯНУЛ! А ведь именно она и формирует тот самый "адрес порта", куда пишем .. да, обращением к массиву с индексом. Ибо она разворачивается вот так:

#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )

, где port .. всего лишь индекс.

что же таки тогда "делают первые 3 строки", ась? :)

Строка №3 - ищет таким же преобразованием в своем массиве .. номер ТАЙМЕРА, связанного с этой ногой .. ага, для того чтобы его ВЫКЛЮЧИТЬ. На всякий случай .. пользователь -- оно жеж дэ'билл, он жеж не знает что у него творится в его программе, а вдруг остался включен? Пожгет нафиг порт..

Это и есть "защита от дурака" - "раз"

Строка №4 - ищет тем же самым способом (по массиву с индексом), но уже по своему массиву .. номер бита в порту, соответствующий этой ноге.

Ну и строка №5 -- не, она ищет НАЗВАНИЕ порта, как индекс для последующего поиска ЕГО АДРЕСА.

Ну и строка №8 -- это ещё одна защита от дурака: а вдруг вы указали "не ту ногу" .. ещё один де'биллизм авторов, посколку проверки на выход ЗА границы массивов - нигде нет, что возможно для начинающего программиста куда как чаще. Ибо для расхожих плат нумерация ног идет "подряд" и "не той ноги" практически не бывает и вовсе.

P.S.

Эта "чудесная фишка", которую так превознес wdrakula - нумеровать ноги "подряд", дабы потом судорожно лазить по даташиту (они там ИМЕНОВАНЫ!) и приводит к ко всей этой галиматье. Кто-то уже считал в сети, что вызов digitalWrite() на мега2560 занимает .. около 400 тактов МК. Я - не пересчитывал, но верю. Двойная косвенность поиска адреса порта связана с желанием .. уменьшить размер массивов.

P.P.S.

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

Это первое, что я нашел примерно полтора года назад, когда впервые взял в руки плату и скачал Ардуино ИДЕ .. не устаю поражаться современным "программистам": Вы пользуетесь чужими разработками и даже не знаете что вам подсовывают в исходниках! Мало того, Вы даже и не знаете где они лежат зачастую .. про "даташиты" - я вообще молчу. Вам тут уже с месяц про них поют.. все "божья роса".

LastHopeMan
Offline
Зарегистрирован: 19.09.2016

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

nik182
Offline
Зарегистрирован: 04.05.2015

Arhat109-2 пишет:

ну .. где-то тут на мой скромный взгляд:

static void turnOffPWM(uint8_t timer)
{
	switch (timer)
	{
		#if defined(TCCR1A) && defined(COM1A1)
		case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
		#endif
		#if defined(TCCR1A) && defined(COM1B1)
		case TIMER1B:   cbi(TCCR1A, COM1B1);    break;
		#endif
		#if defined(TCCR1A) && defined(COM1C1)
		case TIMER1C:   cbi(TCCR1A, COM1C1);    break;
		#endif
		
		#if defined(TCCR2) && defined(COM21)
		case  TIMER2:   cbi(TCCR2, COM21);      break;
		#endif
		
		#if defined(TCCR0A) && defined(COM0A1)
		case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
		#endif
		
		#if defined(TIMER0B) && defined(COM0B1)
		case  TIMER0B:  cbi(TCCR0A, COM0B1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2A1)
		case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2B1)
		case  TIMER2B:  cbi(TCCR2A, COM2B1);    break;
		#endif
		
		#if defined(TCCR3A) && defined(COM3A1)
		case  TIMER3A:  cbi(TCCR3A, COM3A1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3B1)
		case  TIMER3B:  cbi(TCCR3A, COM3B1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3C1)
		case  TIMER3C:  cbi(TCCR3A, COM3C1);    break;
		#endif

		#if defined(TCCR4A) && defined(COM4A1)
		case  TIMER4A:  cbi(TCCR4A, COM4A1);    break;
		#endif					
		#if defined(TCCR4A) && defined(COM4B1)
		case  TIMER4B:  cbi(TCCR4A, COM4B1);    break;
		#endif
		#if defined(TCCR4A) && defined(COM4C1)
		case  TIMER4C:  cbi(TCCR4A, COM4C1);    break;
		#endif			
		#if defined(TCCR4C) && defined(COM4D1)
		case TIMER4D:	cbi(TCCR4C, COM4D1);	break;
		#endif			
			
		#if defined(TCCR5A)
		case  TIMER5A:  cbi(TCCR5A, COM5A1);    break;
		case  TIMER5B:  cbi(TCCR5A, COM5B1);    break;
		case  TIMER5C:  cbi(TCCR5A, COM5C1);    break;
		#endif
	}
}

ежели вчё, оператор switch() компилятыр приобразуит в серию последовательных проверок .. :)

Точно в серию? Почему у меня мой switch() преобразовал в один jmp по таблице?

nik182
Offline
Зарегистрирован: 04.05.2015

Упс..

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Упс. Проверил. Конкретно этот switch() действительно разворачивается в цикл и переходы по таблице .. занятно, надо будет разобраться. Спасибо.

Когда делал свой twi.c, то обнаружил ровно обратный эффект: switch() разворачивается исключительно в кучу операторов if() и заставить развернуть в обход по таблице мне тогда его так и не удалось. В то время (март-апрель 2016), где-то читал, что типа avr-gcc "так не умеет". Ну, стало быть тоже повелся .. "на заборах и не такое пишут".

Значит предыдущий пост компилируется в более компактный код чем я считал. Но, это никак не отменяет всего остального: в худшем случае, цикл прокручивается весь, и это долго. Просто тут применение pwmOff() в целом, во первых нецелесообразно ибо пины или используется как ногодрыг или как аппаратная функция. В одном скетче увидеть и то и другое - достаточно редко, а кроме того, учитывая что библиотеки wiring - это по большей части программная эмуляция, то и вовсе "нонсенс". А во-вторых, такое выключение можно сделать одной командой после того как перекодировали номер пина на номер таймера в строке №3. Уже есть "какой таймер" и точно также можно получить "какой его вывод" и выключить одной командой без перебора и вовсе.

Собственно, по большому счету тут даже и не важно КАК компилируется этот switch(). Все одно: много, долго и незачем.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

LastHopeMan пишет:

Удачи вам в ваших великих достижениях.

И Вам того же!