Что "стряслось" с компилятором?!?

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

Вот текстовка хидера:


#include <inttypes.h>

#define pin50 {PORTB,3}

typedef struct{
  uint16_t port;
  uint8_t  mask;
} Pin;

extern "C"
{
  void setPin(Pin p, uint8_t val){
    if(val) p.port |= p.mask;
    else    p.port &= ~p.mask;
  }
}

Вот скетч:

#include "pins.h"

Pin intPin = pin50;

void setup() {
}

volatile uint8_t val = 0;

void loop() {
  val = 1-val;
  intPin.port = PORTL;

  setPin(intPin, val);
  setPin(pin50, val);
}

Как видим нет ничего "крамольного", один голимый wiring.. :) А это то, что получается с опцией -Wa,-a=test.asm

   8               		.text
  14               	setPin:
  24 0000 0895      		ret

  47               	loop:
  56 0000 9091 0000 		lds r25,val
  57 0004 81E0      		ldi r24,lo8(1)
  58 0006 891B      		sub r24,r25
  59 0008 8093 0000 		sts val,r24

  61 000c 8091 0B01 		lds r24,267
  62 0010 90E0      		ldi r25,0
  63 0012 9093 0000 		sts intPin+1,r25
  64 0016 8093 0000 		sts intPin,r24

  66 001a 8091 0000 		lds r24,val

  69 001e 8091 0000 		lds r24,val
  70 0022 85B1      		in r24,0x5

  72 0024 0895      		ret

  78               	_GLOBAL__sub_I_setPin:
  90 0000 85B1      		in r24,0x5
  91 0002 90E0      		ldi r25,0
  92 0004 9093 0000 		sts intPin+1,r25
  93 0008 8093 0000 		sts intPin,r24
  94 000c 0895      		ret

Кто-то может объяснить "что тут произошло"?

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

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

А в чём проблема? Всё, вроде, правильно.  Он функцию как inline развернул. Не нравится - запретите. Кстати, Вы уверены, что хотите передавать структуру именно по значению, а не по ссылке? Вроде ж Вы там за эффективность боретесь?

Проблема разворота макроса с переменными решается гораздо проще (могу показать), но если Вам так нравится, решайте так.

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

Нашел. Он Lvalue исключил "за ненадобностью". В setPin() пропустил *. Надо было так:

void setPin(Pin p, uint8_t val)
{
  if(val) *(p.port) |= p.mask;
  else    *(p.port) &= ~p.mask;
}

Не, это так .. в порядке "изысканий" компромисса между моим подходом в arhat.h, запрещающего хранить номера пинов в памяти и километровыми таблицами перекодировки в wiring, съедающими на нет всю прелесть ногодрыгов. :)

Просто появилась идейка как можно совместить "приятное с полезным"... проверяю.

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

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

Кстати, Вы какой режим выдачи предупреждений компилятора ставите в IDE? По умолчанию там none, чтобы чайники не пугались. В итоге можно соершенно "молча" переопределить задефайненную константу и потом голову сломать в поисках проблемы. У меня всегда стоит "all". Так вот, если влкючить уровень warning'ов "all", то вот такой скетч:

#include "arhat.h" 
void setup(void) {}
void loop(void){}

Выдаёт вот такую простыню:

Изменена опция сборки, пересобираем все
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c: In function 'pcint_micros':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c:612:39: warning: unused parameter 'oldBit' [-Wunused-parameter]
 void pcint_micros( void *ptr, uint8_t oldBit )
                                       ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c: In function 'time_micros':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c:150:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c: In function 'adcRead':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\arhat.c:426:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\dht22.c: In function 'dht22_read':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\dht22.c:61:9: warning: comparison is always true due to limited range of data type [-Wtype-limits]
         if ( i >= 0 && (i & 1) ){
         ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tcs3200.c: In function 'tcsRun':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tcs3200.c:252:7: warning: comparison is always true due to limited range of data type [-Wtype-limits]
       if( tcsMeasure >= TCS_MAX_MEASURES-1 )                     // Завершены все попытки:
       ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c: In function 'empty':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c:12:18: warning: unused parameter '_tsc' [-Wunused-parameter]
 void empty(void *_tsc) { return; }      // пропуск действия
                  ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c: In function 'tsc_next':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c:46:3: warning: '__progmem__' attribute ignored [-Wattributes]
   const TSC_Step * current PROGMEM = _tsc->table + _tsc->state;
   ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c: In function 'tsc_step':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c:73:7: warning: '__progmem__' attribute ignored [-Wattributes]
       const TSC_Step * current PROGMEM = _tsc->table + _tsc->state;
       ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c: In function 'tsc_micro_next':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c:98:3: warning: '__progmem__' attribute ignored [-Wattributes]
   const TSC_Step * current PROGMEM = _tsc->table + _tsc->state;
   ^
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c: In function 'tsc_micro_step':
C:\Users\Admin\Google Диск\Soft\libraries\arhat\tsc.c:117:7: warning: '__progmem__' attribute ignored [-Wattributes]
       const TSC_Step * current PROGMEM = _tsc->table + _tsc->state;
       ^

Sketch uses 380 bytes (1%) of program storage space. Maximum is 32 256 bytes.
Global variables use 8 bytes (0%) of dynamic memory, leaving 2 040 bytes for local variables. Maximum is 2 048 bytes.

Сами понимаете, сообщения типа "comparison is alwaystrue" больше похожи на ошибки, чем на предупреждения.

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

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

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

ЕвгенийП пишет:
Ну, в любом случае, переменные в номерах пинов можно "разрешить" гораздо проще.

Поделитесь? :)

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

Да, не вопрос, сейчас, минутку ...

Но там сделано не как универсальная библиотека, "на коленке", "для себя", "по быстрому". Поддерживаются только 328 и тини25/45/85 - другое не требовалось. Но принцип поймёте. Сейчас ...

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

Вот, смотрите:

/////////////////////////////////////////////////////////////////////////////////
//	
//	1. Не является универсальной библиотекой и не предназначена для распространения
//	2. Групповые операции доступны только если оперелить #define GROUP_OPERATIONS
//	  2.1 РИСК групповых операций - они НЕ ПРОВЕРЯЮТ, что все пины от одного и того 
//	      же порта, просто берут порт первого пина и сичтают, что остальные пины там же
//   2.2 БЕНЕФИТ групповых операций. Они сделаны так, что все пины изменяются
//       точно одновременно - ОДНОЙ командой процессора. Иногда это важно и в этих
//       случаях лучше воспользоваться digitalWrite на два пина, чем двумя по пину,
//       хотя последнее и эффективнее. Потому, кстати, нет никакого смысла в 
//       мультипортовых групповых операциях - два порта одной командой не изменишь.
//
//   3. Если этот текст как-то попал к Вам, это Ваши проблемы. Автор не несёт никаких
//      обязательств. Текст исключительно для внутреннего использования.
//
// Используем операции с одним подчерком. Операции с большим количество подчерков - внутренняя кухня
//
//	_pinMode(pin,state)
//	_digitalWrite(pin,val)
//	_digitalRead(pin)
//	_pulseHigh(pin)
//	_pulseLow(pin)
//	_pinPulse(pin)
//	_pinInvert(pin)
//
// Групповые операции N - 2-8
//	_digitalWriteN(p1,...,pN,val)
//	_pulseHighN(p1,...,pN)
//	_pulseLowN(p1,...,pN)
//	_pinInvertN(p1,...,pN)
//	_pinPulseN(p1,...,pN)
//	_digitalReadN(p1,...,pN) // выдаёт байт, пины можно снимать битовой маской
//
//	
/////////////////////////////////////////

#ifndef DigitalPins_h
#define DigitalPins_h

#ifndef INPUT
	#define INPUT 0x0
#endif // !INPUT

#ifndef OUTPUT
	#define OUTPUT 0x1
#endif // !OUTPUT

#ifndef INPUT_PULLUP
	#define INPUT_PULLUP 0x2
#endif // !INPUT_PULLUP

#ifndef HIGH
	#define HIGH 0x1
#endif // !HIGH

#ifndef LOW
	#define LOW 0x0
#endif // !LOW

#ifdef uint8_t
	typedef	uint8_t	unsigned char
#endif // uint8_t


#define	__AVR_ATtinyDetected__	(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__))

#if (!__AVR_ATtinyDetected__) && (!defined(__AVR_ATmega328P__))
	#ifndef IGNORE_CPU_CHECK
		#error The library has been well tested for ATmega328P and ATtiny25/45/85 only. Define IGNORE_CPU_CHECK option to ignore this check.
	#endif // !IGNORE_CPU_CHECK
#endif // (!__AVR_ATtinyDetected__) && (!defined(__AVR_ATmega328P__))



//////////////////////////////////////////////////////////////
//
//	Маски для битов групповых операций
//
#define __orMask2(m1,m2)	((uint8_t)((m1) | (m2)))
#define __orMask3(m1,m2,m3)	((uint8_t)(__orMask2(m1,m2) | (m3)))
#define __orMask4(m1,m2,m3,m4)	((uint8_t)(__orMask3(m1,m2,m3) | (m4)))
#define __orMask5(m1,m2,m3,m4,m5)	((uint8_t)(__orMask4(m1,m2,m3,m4) | (m5)))
#define __orMask6(m1,m2,m3,m4,m5,m6)	((uint8_t)(__orMask5(m1,m2,m3,m4,m5) | (m6)))
#define __orMask7(m1,m2,m3,m4,m5,m6,m7)	((uint8_t)(__orMask6(m1,m2,m3,m4,m5,m6) | (m7)))
#define __orMask8(m1,m2,m3,m4,m5,m6,m7,m8)	((uint8_t)(__orMask7(m1,m2,m3,m4,m5,m6,m7) | (m8)))


#define	ERROR_BIT	255

#if __AVR_ATtinyDetected__

	//////////////////////////////////////////////////////////////
	//
	//	У ATtiny есть единственный порт B, 
	// а номера пинов совпадают с номерами битов
	//
	#define __pin2Port(pin)	(PORTB)
	#define __pin2DirectionPort(pin)	(DDRB)
	#define __pin2InputPort(pin)	(PINB)
	#define __pin2PortBit(pin)	(pin)

#else	//	Не __AVR_ATtinyDetected__

	//////////////////////////////////////////////////////////////
	//
	//	Выбор порта  и бита по пину для ATmega328
	//
	#define	ERROR_PORT	_SFR_IO8(255)

	#define ___pin2Port(pin, portD, portB, portC)	\
		((/*(pin) >= 0 && */(pin) < 8) ? (portD) : \
			(((pin) >= 8 && (pin) < 14) ? (portB) : \
				(((pin) >= A0 && (pin) <= A7) ? (portC) : ERROR_PORT)))

	#define __pin2Port(pin) ___pin2Port(pin, PORTD, PORTB, PORTC)
	#define __pin2DirectionPort(pin) ___pin2Port(pin, DDRD, DDRB, DDRC)
	#define __pin2InputPort(pin) ___pin2Port(pin,PIND,PINB,PINC)

	#define	__pin2PortBit(pin)	\
		((/*(pin) >= 0 && */(pin) < 8) ? (pin) : \
			(((pin) >= 8 && (pin) < 14) ? ((pin)-8) : \
				(((pin) >= A0 && (pin) <= A5) ? ((pin)-A0) : ERROR_BIT)))

#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Битовые маски из номеров пинов
//
#define __pin2Mask(pin)	(1 << __pin2PortBit(pin))
#define __orPin2(p1,p2) __orMask2(__pin2Mask(p1),__pin2Mask(p2))
#define __orPin3(p1,p2,p3) __orMask3(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3))
#define __orPin4(p1,p2,p3,p4) __orMask4(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3),__pin2Mask(p4))
#define __orPin5(p1,p2,p3,p4,p5) __orMask5(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3),__pin2Mask(p4),__pin2Mask(p5))
#define __orPin6(p1,p2,p3,p4,p5,p6) __orMask6(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3),__pin2Mask(p4),__pin2Mask(p5),__pin2Mask(p6))
#define __orPin7(p1,p2,p3,p4,p5,p6,p7) __orMask7(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3),__pin2Mask(p4),__pin2Mask(p5),__pin2Mask(p6),__pin2Mask(p7))
#define __orPin8(p1,p2,p3,p4,p5,p6,p7,p8) __orMask8(__pin2Mask(p1),__pin2Mask(p2),__pin2Mask(p3),__pin2Mask(p4),__pin2Mask(p5),__pin2Mask(p6),__pin2Mask(p7),__pin2Mask(p8))

//////////////////////////////////////////////////////////////
//
//	Внутренняя реализация основных операций
//
#define __pinHigh(port,mask) ((port) |= (mask))
#define __pinLow(port,mask) ((port) &= ~(mask))
#define	__digitalWrite(port,mask,val) (((val)!=LOW) ? (__pinHigh(port,mask)) : (__pinLow(port,mask)))
#define	__pinInvert(port,mask) ((port) |= (mask))
#define __pinModeOutput(dirport,mask) ((dirport) |= (mask))
#define __pinModeInput(dirport, mask)  ((dirport) &= ~(mask))
#define __pinModeInputPullUp(port,dirport,mask) { uint8_t oldSREG = SREG; cli(); (dirport) &= ~(mask); (port) |= (mask); SREG = oldSREG; }
#define __pinMode(pin,state,mask) ((state) == INPUT) ? (__pinModeInput(__pin2DirectionPort(pin), mask)) : \
		((state) == INPUT_PULLUP) ? (__pinModeInputPullUp(__pin2Port(pin), __pin2DirectionPort(pin), mask)) : \
		((state) == OUTPUT) ? (__pinModeOutput(__pin2DirectionPort(pin), mask)) : (__pinModeOutput(__pin2DirectionPort(pin),0))


//////////////////////////////////////////////////////////////
//
//	Основные операции с единичным пином
//
#define _pinMode(pin,state) __pinMode(pin,state,__pin2Mask(pin))
#define	_digitalWrite(pin,val) __digitalWrite(__pin2Port(pin),__pin2Mask(pin),val)
#define	_digitalRead(pin)	(((__pin2InputPort(pin)) & (__pin2Mask(pin))) ? HIGH : LOW)
#define _pulseHigh(pin) do { uint8_t oldSREG = SREG; cli(); _digitalWrite(pin,HIGH); _digitalWrite(pin,LOW); SREG = oldSREG; } while(false)
#define	_pulseLow(pin)  do { uint8_t oldSREG = SREG; cli(); _digitalWrite(pin,LOW); _digitalWrite(pin,HIGH); SREG = oldSREG; } while(false)
#define	_pinPulse(pin)  do { uint8_t oldSREG = SREG; cli(); _pinInvert(pin); _pinInvert(pin); SREG = oldSREG; } while(false)
#define	_pinInvert(pin) __pinInvert(__pin2InputPort(pin),__pin2Mask(pin))


#if defined(GROUP_OPERATIONS)

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	ГРУППОВЫЕ ОРПЕРАЦИИ
//
//
//	Групповой pulseLow по маске (внутренняя операция)
//
#define	__pulseLow(port,mask)	\
	asm volatile (	\
		"in __tmp_reg__,__SREG__	\n\t"	\
		"cli	\n\t"	\
		"in r24,%0	\n\t"	\
		"mov r23,r24 \n\t"	\
		"or r24,%1	\n\t"	\
		"com %1	\n\t"	\
		"and r23,%1 \n\t"	\
		"out %0,r23 \n\t"	\
		"out %0,r24 \n\t"	\
		"out __SREG__,__tmp_reg__	\n\t"	\
		: : "I" (_SFR_IO_ADDR(port)), "r" ((mask)) : "r24", "r23"	\
	)

//////////////////////////////////////////////////////////////
//
//	Групповой pulseHigh по маске (внутренняя операция)
//
#define	__pulseHigh(port,mask)	\
	asm volatile (	\
		"in __tmp_reg__,__SREG__	\n\t"	\
		"cli	\n\t"	\
		"in r24,%0	\n\t"	\
		"mov r23,r24 \n\t"	\
		"or r24,%1	\n\t"	\
		"com %1	\n\t"	\
		"and r23,%1 \n\t"	\
		"out %0,r24 \n\t"	\
		"out %0,r23 \n\t"	\
		"out __SREG__,__tmp_reg__	\n\t"	\
		: : "I" (_SFR_IO_ADDR(port)), "r" ((mask)) : "r24", "r23"	\
	)

//////////////////////////////////////////////////////////////
//
//	Групповой Invert по маске (внутренняя операция)
//
#define	__Invert(port,mask)	\
	asm volatile (	\
		"in __tmp_reg__,__SREG__	\n\t"	\
		"cli	\n\t"	\
		"in r24,%0	\n\t"	\
		"or r24,%1	\n\t"	\
		"out %0,r24 \n\t"	\
		"out __SREG__,__tmp_reg__	\n\t"	\
		: : "I" (_SFR_IO_ADDR(port)), "r" ((mask)) : "r24"	\
	)

//////////////////////////////////////////////////////////////
//
//	Групповой двойной Invert по маске (внутренняя операция)
//
#define	__2Invert(port,mask)	\
	asm volatile (	\
		"in __tmp_reg__,__SREG__	\n\t"	\
		"cli	\n\t"	\
		"in r24,%0	\n\t"	\
		"or r24,%1	\n\t"	\
		"out %0,r24 \n\t"	\
		"out %0,r24 \n\t"	\
		"out __SREG__,__tmp_reg__	\n\t"	\
		: : "I" (_SFR_IO_ADDR(port)), "r" ((mask)) : "r24"	\
	)


//////////////////////////////////////////////////////////////
//
//	Групповой _digitalWrite
//
#define	_digitalWrite2(p1,p2,val) __digitalWrite(__pin2Port(p1),__orPin2(p1,p2),val)
#define	_digitalWrite3(p1,p2,p3,val) __digitalWrite(__pin2Port(p1),__orPin3(p1,p2,p3),val)
#define	_digitalWrite4(p1,p2,p3,p4,val) __digitalWrite(__pin2Port(p1),__orPin4(p1,p2,p3,p4),val)
#define	_digitalWrite5(p1,p2,p3,p4,p5,val) __digitalWrite(__pin2Port(p1),__orPin5(p1,p2,p3,p4,p5),val)
#define	_digitalWrite6(p1,p2,p3,p4,p5,p6,val) __digitalWrite(__pin2Port(p1),__orPin6(p1,p2,p3,p4,p5,p6),val)
#if !__AVR_ATtinyDetected__
#define	_digitalWrite7(p1,p2,p3,p4,p5,p6,p7,val) __digitalWrite(__pin2Port(p1),__orPin7(p1,p2,p3,p4,p5,p6,p7),val)
#define	_digitalWrite8(p1,p2,p3,p4,p5,p6,p7,p8,val) __digitalWrite(__pin2Port(p1),__orPin8(p1,p2,p3,p4,p5,p6,p7,p8),val)
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Групповой _pulseHigh
//
#define _pulseHigh2(p1,p2) __pulseHigh(__pin2Port(p1),__orPin2(p1,p2))
#define _pulseHigh3(p1,p2,p3) __pulseHigh(__pin2Port(p1),__orPin3(p1,p2,p3))
#define _pulseHigh4(p1,p2,p3,p4) __pulseHigh(__pin2Port(p1),__orPin4(p1,p2,p3,p4))
#define _pulseHigh5(p1,p2,p3,p4,p5) __pulseHigh(__pin2Port(p1),__orPin5(p1,p2,p3, p4, p5))
#define _pulseHigh6(p1,p2,p3,p4,p5,p6) __pulseHigh(__pin2Port(p1),__orPin6(p1,p2, p3, p4, p5, p6))
#if !__AVR_ATtinyDetected__
#define _pulseHigh7(p1,p2,p3,p4,p5,p6,p7) __pulseHigh(__pin2Port(p1),__orPin7(p1, p2, p3, p4, p5, p6, p7))
#define _pulseHigh8(p1,p2,p3,p4,p5,p6,p7,p8) __pulseHigh(__pin2Port(p1),__orPin8(p1, p2, p3, p4, p5, p6, p7, p8))
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Групповой _pulseLow
//
#define _pulseLow2(p1,p2) __pulseLow(__pin2Port(p1),__orPin2(p1,p2))
#define _pulseLow3(p1,p2,p3) __pulseLow(__pin2Port(p1),__orPin3(p1,p2,p3))
#define _pulseLow4(p1,p2,p3,p4) __pulseLow(__pin2Port(p1),__orPin4(p1,p2,p3,p4))
#define _pulseLow5(p1,p2,p3,p4,p5) __pulseLow(__pin2Port(p1),__orPin5(p1,p2,p3, p4, p5))
#define _pulseLow6(p1,p2,p3,p4,p5,p6) __pulseLow(__pin2Port(p1),__orPin6(p1,p2, p3, p4, p5, p6))
#if !__AVR_ATtinyDetected__
#define _pulseLow7(p1,p2,p3,p4,p5,p6,p7) __pulseLow(__pin2Port(p1),__orPin7(p1, p2, p3, p4, p5, p6, p7))
#define _pulseLow8(p1,p2,p3,p4,p5,p6,p7,p8) __pulseLow(__pin2Port(p1),__orPin8(p1, p2, p3, p4, p5, p6, p7, p8))
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Групповой _pinInvert
//
#define _pinInvert2(p1,p2) __Invert(__pin2InputPort(p1),__orPin2(p1,p2))
#define _pinInvert3(p1,p2,p3) __Invert(__pin2InputPort(p1),__orPin3(p1,p2,p3))
#define _pinInvert4(p1,p2,p3,p4) __Invert(__pin2InputPort(p1),__orPin4(p1,p2,p3, p4))
#define _pinInvert5(p1,p2,p3,p4,p5) __Invert(__pin2InputPort(p1),__orPin5(p1,p2, p3, p4, p5))
#define _pinInvert6(p1,p2,p3,p4,p5,p6) __Invert(__pin2InputPort(p1),__orPin6(p1, p2, p3, p4, p5, p6))
#if !__AVR_ATtinyDetected__
#define _pinInvert7(p1,p2,p3,p4,p5,p6,p7) __Invert(__pin2InputPort(p1),__orPin7(p1, p2, p3, p4, p5, p6, p7))
#define _pinInvert8(p1,p2,p3,p4,p5,p6,p7,p8) __Invert(__pin2InputPort(p1),__orPin8(p1, p2, p3, p4, p5, p6, p7, p8))
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Групповой _pinPulse
//
#define _pinPulse2(p1,p2) __2Invert(__pin2InputPort(p1),__orPin2(p1,p2))
#define _pinPulse3(p1,p2,p3) __2Invert(__pin2InputPort(p1),__orPin3(p1,p2,p3))
#define _pinPulse4(p1,p2,p3,p4) __2Invert(__pin2InputPort(p1),__orPin4(p1,p2,p3, p4))
#define _pinPulse5(p1,p2,p3,p4,p5) __2Invert(__pin2InputPort(p1),__orPin5(p1,p2, p3, p4, p5))
#define _pinPulse6(p1,p2,p3,p4,p5,p6) __2Invert(__pin2InputPort(p1),__orPin6(p1, p2, p3, p4, p5, p6))
#if !__AVR_ATtinyDetected__
#define _pinPulse7(p1,p2,p3,p4,p5,p6,p7) __2Invert(__pin2InputPort(p1),__orPin7(p1, p2, p3, p4, p5, p6, p7))
#define _pinPulse8(p1,p2,p3,p4,p5,p6,p7,p8) __2Invert(__pin2InputPort(p1),__orPin8(p1, p2, p3, p4, p5, p6, p7, p8))
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Групповой _digitalRead
//
#define	__read(port, mask) ((port) & (mask))
#define	_digitalRead2(p1,p2) __read(__pin2InputPort(p1),__orPin2(p1,p2))
#define	_digitalRead3(p1,p2,p3) __read(__pin2InputPort(p1),__orPin3(p1,p2,p3))
#define	_digitalRead4(p1,p2,p3,p4) __read(__pin2InputPort(p1),__orPin4(p1,p2,p3,p4))
#define	_digitalRead5(p1,p2,p3,p4,p5) __read(__pin2InputPort(p1),__orPin5(p1,p2,p3,p4,p5))
#define	_digitalRead6(p1,p2,p3,p4,p5,p6) __read(__pin2InputPort(p1),__orPin6(p1,p2,p3,p4,p5,p6))
#if !__AVR_ATtinyDetected__
#define	_digitalRead7(p1,p2,p3,p4,p5,p6,p7) __read(__pin2InputPort(p1),__orPin7(p1,p2,p3,p4,p5,p6,p7))
#define	_digitalRead8(p1,p2,p3,p4,p5,p6,p7,p8) __read(__pin2InputPort(p1),__orPin8(p1,p2,p3,p4,p5,p6,p7,p8))
#endif	//	__AVR_ATtinyDetected__

//////////////////////////////////////////////////////////////
//
//	Маски для проверки отдельных битов после группового _digitalRead
//
#define _pin0	__pin2Mask(0)
#define _pin1	__pin2Mask(1)
#define _pin2	__pin2Mask(2)
#define _pin3	__pin2Mask(3)
#define _pin4	__pin2Mask(4)
#define _pin5	__pin2Mask(5)
#if !__AVR_ATtinyDetected__
	#define _pin6	__pin2Mask(6)
	#define _pin7	__pin2Mask(7)
	#define _pin8	__pin2Mask(8)
	#define _pin9	__pin2Mask(9)
	#define _pin10	__pin2Mask(10)
	#define _pin11	__pin2Mask(11)
	#define _pin12	__pin2Mask(12)
	#define _pin13	__pin2Mask(13)
	#define _pinA0	__pin2Mask(A0)
	#define _pinA1	__pin2Mask(A1)
	#define _pinA2	__pin2Mask(A2)
	#define _pinA3	__pin2Mask(A3)
	#define _pinA4	__pin2Mask(A4)
	#define _pinA5	__pin2Mask(A5)
#endif	//	__AVR_ATtinyDetected__

#endif //	defined(GROUP_OPERATIONS)


#endif	//	DigitalPins_h

Вот, смотрите, если Вы проследите всю цепочку от _digitalWrite() и выше, то увидите, что за выбор порта и бита отвечают макросы в строках 108-122.

А эти макросы написаны так, что если им подсунуть константу, они её вернут и получится digitalWrite одной машинной командой. А если им подсунуть переменную, то эти макросы развернутся в некий код по поределению порта и бита по значению этой переменной. От программиста ничего не требуется - само развернётся как надо в зависимости от обстоятельств.

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

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

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

Спасибо. Этот подход понятен и давно известен.

Вот что-то похожее пробую упростить для второй части: когда номер пина в переменной. У такого решения есть существенный недостаток - "само развернется в некий код". И так "при каждом обращении". Мне (и не только) показалось что слишком накладно. Если у wiring огромные затраты на перекодирование по таблицам (на размер самих таблиц, для меги2560 в частности), то код растет не настолько шустро и не настолько теряет в скорости как этот подход. Это уже обсуждалось на Cyber-place в процессе развития Cyberlib.

Как пример, находил на просторах Сети, ещё с год назад digitalWriteFast.h:

#include "WProgram.h" 
#include <wiring.h>

#define BIT_READ(value, bit) (((value) >> (bit)) & 0x01)
#define BIT_SET(value, bit) ((value) |= (1UL << (bit)))
#define BIT_CLEAR(value, bit) ((value) &= ~(1UL << (bit)))
#define BIT_WRITE(value, bit, bitvalue) (bitvalue ? BIT_SET(value, bit) : BIT_CLEAR(value, bit))

#if !defined(digitalPinToPortReg)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Arduino Mega Pins
#define digitalPinToPortReg(P) \
(((P) >= 22 && (P) <= 29) ? &PORTA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \
(((P) >= 30 && (P) <= 37) ? &PORTC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PORTE : \
(((P) >= 54 && (P) <= 61) ? &PORTF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \
(((P) == 14 || (P) == 15) ? &PORTJ : \
(((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))

#define digitalPinToDDRReg(P) \
(((P) >= 22 && (P) <= 29) ? &DDRA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \
(((P) >= 30 && (P) <= 37) ? &DDRC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &DDRE : \
(((P) >= 54 && (P) <= 61) ? &DDRF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \
(((P) == 14 || (P) == 15) ? &DDRJ : \
(((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL))))))))))

#define digitalPinToPINReg(P) \
(((P) >= 22 && (P) <= 29) ? &PINA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \
(((P) >= 30 && (P) <= 37) ? &PINC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \
((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PINE : \
(((P) >= 54 && (P) <= 61) ? &PINF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \
(((P) == 14 || (P) == 15) ? &PINJ : \
(((P) >= 62 && (P) <= 69) ? &PINK : &PINL))))))))))

#define __digitalPinToBit(P) \
(((P) >=  7 && (P) <=  9) ? (P) - 3 : \
(((P) >= 10 && (P) <= 13) ? (P) - 6 : \
(((P) >= 22 && (P) <= 29) ? (P) - 22 : \
(((P) >= 30 && (P) <= 37) ? 37 - (P) : \
(((P) >= 39 && (P) <= 41) ? 41 - (P) : \
(((P) >= 42 && (P) <= 49) ? 49 - (P) : \
(((P) >= 50 && (P) <= 53) ? 53 - (P) : \
(((P) >= 54 && (P) <= 61) ? (P) - 54 : \
(((P) >= 62 && (P) <= 69) ? (P) - 62 : \
(((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \
(((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \
(((P) == 19) ? 2 : \
(((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \
(((P) == 2) ? 4 : \
(((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))

// 15 PWM
#define __digitalPinToTimer(P) \
(((P) == 13 || (P) ==  4) ? &TCCR0A : \
(((P) == 11 || (P) == 12) ? &TCCR1A : \
(((P) == 10 || (P) ==  9) ? &TCCR2A : \
(((P) ==  5 || (P) ==  2 || (P) ==  3) ? &TCCR3A : \
(((P) ==  6 || (P) ==  7 || (P) ==  8) ? &TCCR4A : \
(((P) == 46 || (P) == 45 || (P) == 44) ? &TCCR5A : 0))))))
#define __digitalPinToTimerBit(P) \
(((P) == 13) ? COM0A1 : (((P) ==  4) ? COM0B1 : \
(((P) == 11) ? COM1A1 : (((P) == 12) ? COM1B1 : \
(((P) == 10) ? COM2A1 : (((P) ==  9) ? COM2B1 : \
(((P) ==  5) ? COM3A1 : (((P) ==  2) ? COM3B1 : (((P) ==  3) ? COM3C1 : \
(((P) ==  6) ? COM4A1 : (((P) ==  7) ? COM4B1 : (((P) ==  8) ? COM4C1 : \
(((P) == 46) ? COM5A1 : (((P) == 45) ? COM5B1 : COM5C1))))))))))))))

#else

// Standard Arduino Pins
#define digitalPinToPortReg(P) \
(((P) >= 0 && (P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC))
#define digitalPinToDDRReg(P) \
(((P) >= 0 && (P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC))
#define digitalPinToPINReg(P) \
(((P) >= 0 && (P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC))
#define __digitalPinToBit(P) \
(((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14))

#if defined(__AVR_ATmega8__)
// 3 PWM
#define __digitalPinToTimer(P) \
(((P) ==  9 || (P) == 10) ? &TCCR1A : (((P) == 11) ? &TCCR2 : 0))
#define __digitalPinToTimerBit(P) \
(((P) ==  9) ? COM1A1 : (((P) == 10) ? COM1B1 : COM21))
#else  //168,328

// 6 PWM
#define __digitalPinToTimer(P) \
(((P) ==  6 || (P) ==  5) ? &TCCR0A : \
(((P) ==  9 || (P) == 10) ? &TCCR1A : \
(((P) == 11 || (P) ==  3) ? &TCCR2A : 0)))
#define __digitalPinToTimerBit(P) \
(((P) ==  6) ? COM0A1 : (((P) ==  5) ? COM0B1 : \
(((P) ==  9) ? COM1A1 : (((P) == 10) ? COM1B1 : \
(((P) == 11) ? COM2A1 : COM2B1)))))
#endif  //defined(__AVR_ATmega8__)


#endif  //mega
#endif  //#if !defined(digitalPinToPortReg)




#define __atomicWrite__(A,P,V) \
if ( (int)(A) < 0x40) { bitWrite(*(A), __digitalPinToBit(P), (V) );}  \
else {                                                         \
uint8_t register saveSreg = SREG;                          \
cli();                                                     \
bitWrite(*(A), __digitalPinToBit(P), (V) );                   \
SREG=saveSreg;                                             \
} 


#ifndef digitalWriteFast
#define digitalWriteFast(P, V) \
do {                       \
if (__builtin_constant_p(P) && __builtin_constant_p(V))   __atomicWrite__((uint8_t*) digitalPinToPortReg(P),P,V) \
else  digitalWrite((P), (V));         \
}while (0)
#endif  //#ifndef digitalWriteFast2

#if !defined(pinModeFast)
#define pinModeFast(P, V) \
do {if (__builtin_constant_p(P) && __builtin_constant_p(V)) __atomicWrite__((uint8_t*) digitalPinToDDRReg(P),P,V) \
else pinMode((P), (V)); \
} while (0)
#endif


#ifndef noAnalogWrite
#define noAnalogWrite(P) \
	do {if (__builtin_constant_p(P) )  __atomicWrite((uint8_t*) __digitalPinToTimer(P),P,0) \
		else turnOffPWM((P));   \
} while (0)
#endif		


#ifndef digitalReadFast
	#define digitalReadFast(P) ( (int) _digitalReadFast_((P)) )
	#define _digitalReadFast_(P ) \
	(__builtin_constant_p(P) ) ? ( \
	( BIT_READ(*digitalPinToPINReg(P), __digitalPinToBit(P))) ) : \
	digitalRead((P))
#endif

 

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

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

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

В 80-ые, когда мы делали навигацию для спец. изделий, мы как раз и использовали версию языка ассемблера с очень развитым препроцессором (сами сделали препроцессор из исходников компилятора PL/1 - представляете мощь такого препроцессора?). Вот там действительно можно было нагенерить любой код при сохранении его универсальности. А универсальность была важна, т.к. обычно в одном изделии использовалось несколько разных процессоров (до трёх!). Мы это называли тогда не препроцессором, у "управляемым ассемблером".

Подумайте. может Вам по этому пути пойти, раз уж Вам так хочется сделать универсальную библиотеку? С IDE не знаю, а в АВР_Студию вставить дполонительный шаг обработки кода (пропускание через свой препроцессор) как два пальца. Нормально получилось бы, если по уму сделать.

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

Ну .. ежели помечтать, то мне было бы достаточно в макроорпеделениях условной обработки через defined(), чтобы забабахать универсальный подход без потери эффективности. Там жеж засада только в том, что какой-нибудь DREG##p разворачивается в отсутствующий макрос! Предварительно запросить IF(defined(DREG##p) и все дела. Только вот никак. :)

Есть одна мысля .. пока пробую. Мне кажется что засада в том, что типовые PORTB определены как буквенные константы .. вот ежели определить PORT(n) может оказаться проще.

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

Arhat109-2 пишет:

Ну .. ежели помечтать, то мне было бы достаточно в макроорпеделениях условной обработки через defined(), чтобы забабахать универсальный подход без потери эффективности. Там жеж засада только в том, что какой-нибудь DREG##p разворачивается в отсутствующий макрос! Предварительно запросить IF(defined(DREG##p) и все дела. 

Не сработает. Как только углубитесь, поймёте. Там никак нельзя по if выдать один текст на выход, а по else другой. Точно, не работает, поверьте.

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

Знаю. Я ж "помечтать".. :)

В общем, похоже интересен такой подход:

// файл pins.h:
#include <inttypes.h>
#include <avr/pgmspace.h>

#define pin1  {&PORTB,3}
#define pin2  {1,3}

typedef struct {
  volatile uint8_t * port;
  uint8_t            mask;
} Pin1;

typedef struct{
  uint8_t port;
  uint8_t mask;
} Pin2;

typedef struct{
  uint8_t port:4;
  uint8_t mask:4;
} Pin3;

volatile uint8_t * pin2port[] =
{
  &PORTA, &PORTB, &PORTC, &PORTD, &PORTE, &PORTF, &PORTG, &PORTH, 0, &PORTJ, &PORTK, &PORTL
};

#define setPin1(p, val)             \
{                                   \
  Pin1            tmp  = p;         \
  if(val) *(tmp.port) |= tmp.mask;  \
  else    *(tmp.port) &= ~tmp.mask; \
}

#define setPin2(p, val)                         \
{                                               \
  Pin2               tmp  = p;                  \
  volatile uint8_t * port = pin2port[tmp.port]; \
  if(val) *(port) |= tmp.mask;                  \
  else    *(port) &= ~tmp.mask;                 \
}

#define setPin3(p, val)                         \
{                                               \
  Pin3               tmp  = p;                  \
  volatile uint8_t * port = pin2port[tmp.port]; \
  if(val) *(port) |= 1<<tmp.mask;               \
  else    *(port) &= ~(1<<tmp.mask);            \
}
// проверочный скетч:
#include "pins.h"

Pin1 varPin1 = pin1;
Pin2 varPin2 = pin2;
Pin3 varPin3;
uint8_t varPin4 = 13;


void setup() {
}

volatile uint8_t val = 0;

void loop() {
  val = 1-val;
  varPin3 = {2,2};

  digitalWrite(varPin4, val);
  setPin1(pin1, val);
  setPin1(varPin1, val);
  setPin2(pin2, val);
  setPin2(varPin2, val);
  setPin3(pin2, val);
  setPin3(varPin3, val);
}

Итого:

вар.1 -- структура пина в виде прямого адреса порта и битовой маски. Константные номера = 20байт (6байт), в переменной = 36 байт (18байт).

вар.2 -- структура с индексом порта в массиве и битовой маски. Константно = 28 байт (14байт), с переменной = 46 байт (30). Плюсом массив в 12 слов х 3шт. для ввода/вывода и управления. 3*12*2 = 72 байта, можно в progmem.

вар.3 -- структура пина в виде битовых полей в 1 байте. Константный вариант как ни странно равен предыдущему из-за предвычисления сдвигов. А вот вариант с хранением в переменной практически не отличается от содержательной части digitalWrite() и занимает 78 байт. Плюсом сюда тоже 72 байта флеш.

Примечание: в случае заранее известной установки в 0 или 1, все значительно упрощается. В скобках даны цифирьки для такого упрощения.

P.S. я исходил из того, что номер пина, так или иначе, но где-то в программе задается константным способом. Соответственно, можно в таком месте использовать макропреобразователь числа в структуру пина.

Итого: 1-й способ в случае константных пинов дает код, практически неотличимый от ассемблерного, но требует для хранения каждого пина по 3 байта оперативы. Если оперативы не жалко, а размер кода "жмет" - то это самый компактный способ. И самый шустрый соответственно.

3-й Вариант с упаковкой в битовые поля не дает практически никаких преимуществ перед wiring.

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

Вот .. так как-то. Универсального решения нет и в этом направлении. :)