Что "стряслось" с компилятором?!?
- Войдите на сайт для отправки комментариев
Втр, 03/05/2016 - 11:55
Вот текстовка хидера:
#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
Кто-то может объяснить "что тут произошло"?
Надеюсь идея понятна: вместо простого байта под номер пина отводим структуру из пары "порт,маска" и соответсвенно, получаем единую работу с пинами как в случае их константного определения, таки при хранении в переменной. Хотелось проверить как компилятор позволяет передавать структуры в качестве параметров..
А в чём проблема? Всё, вроде, правильно. Он функцию как inline развернул. Не нравится - запретите. Кстати, Вы уверены, что хотите передавать структуру именно по значению, а не по ссылке? Вроде ж Вы там за эффективность боретесь?
Проблема разворота макроса с переменными решается гораздо проще (могу показать), но если Вам так нравится, решайте так.
Нашел. Он Lvalue исключил "за ненадобностью". В setPin() пропустил *. Надо было так:
void setPin(Pin p, uint8_t val) { if(val) *(p.port) |= p.mask; else *(p.port) &= ~p.mask; }Не, это так .. в порядке "изысканий" компромисса между моим подходом в arhat.h, запрещающего хранить номера пинов в памяти и километровыми таблицами перекодировки в wiring, съедающими на нет всю прелесть ногодрыгов. :)
Просто появилась идейка как можно совместить "приятное с полезным"... проверяю.
Ну, в любом случае, переменные в номерах пинов можно "разрешить" гораздо проще.
Кстати, Вы какой режим выдачи предупреждений компилятора ставите в 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.Сами понимаете, сообщения типа "
comparisonisalwaystrue" больше похожи на ошибки, чем на предупреждения.Это вам ещё мало высыпало .. в 1.6.8 ещё валит кучу варнгингов из-за переопределния препроцессорных констант. По умолчанию выставляю без предупреждений, только ошибки. Но иногда, в процессе отладок тоже любуюсь .. :)
Поделитесь? :)
Да, не вопрос, сейчас, минутку ...
Но там сделано не как универсальная библиотека, "на коленке", "для себя", "по быстрому". Поддерживаются только 328 и тини25/45/85 - другое не требовалось. Но принцип поймёте. Сейчас ...
Вот, смотрите:
///////////////////////////////////////////////////////////////////////////////// // // 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 подсунуть можно) и за отуствие контроля единости порта в групповых операциях, но он сказал, что тогда бы это была не работёнка на два часа, а серьёзный проект. Я согласился и отстал.
Спасибо. Этот подход понятен и давно известен.
Вот что-то похожее пробую упростить для второй части: когда номер пина в переменной. У такого решения есть существенный недостаток - "само развернется в некий код". И так "при каждом обращении". Мне (и не только) показалось что слишком накладно. Если у 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Мне нравится такой подход. Переменные как таковые я использую редко, вот в этих редких случаях оно и подстрахует, чтоб не париться.
Вообще-то, для нормального выхода, здесь просто инструмент неправильный. Препроцессор в С убогий. Попробуйте представить себе, что в препроцессоре есть циклы, переменные и т.п. - насколько проще было бы делать код действительно универсальным и действительно оптимальным. На этап компиляции можно было бы разбираться с универсальностью и оставлять в коде только то, что реально нужно и ни байта больше.
В 80-ые, когда мы делали навигацию для спец. изделий, мы как раз и использовали версию языка ассемблера с очень развитым препроцессором (сами сделали препроцессор из исходников компилятора PL/1 - представляете мощь такого препроцессора?). Вот там действительно можно было нагенерить любой код при сохранении его универсальности. А универсальность была важна, т.к. обычно в одном изделии использовалось несколько разных процессоров (до трёх!). Мы это называли тогда не препроцессором, у "управляемым ассемблером".
Подумайте. может Вам по этому пути пойти, раз уж Вам так хочется сделать универсальную библиотеку? С IDE не знаю, а в АВР_Студию вставить дполонительный шаг обработки кода (пропускание через свой препроцессор) как два пальца. Нормально получилось бы, если по уму сделать.
Ну .. ежели помечтать, то мне было бы достаточно в макроорпеделениях условной обработки через defined(), чтобы забабахать универсальный подход без потери эффективности. Там жеж засада только в том, что какой-нибудь DREG##p разворачивается в отсутствующий макрос! Предварительно запросить IF(defined(DREG##p) и все дела. Только вот никак. :)
Есть одна мысля .. пока пробую. Мне кажется что засада в том, что типовые PORTB определены как буквенные константы .. вот ежели определить PORT(n) может оказаться проще.
Ну .. ежели помечтать, то мне было бы достаточно в макроорпеделениях условной обработки через defined(), чтобы забабахать универсальный подход без потери эффективности. Там жеж засада только в том, что какой-нибудь DREG##p разворачивается в отсутствующий макрос! Предварительно запросить IF(defined(DREG##p) и все дела.
Не сработает. Как только углубитесь, поймёте. Там никак нельзя по if выдать один текст на выход, а по else другой. Точно, не работает, поверьте.
Знаю. Я ж "помечтать".. :)
В общем, похоже интересен такой подход:
// файл 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 байта на номер пина в памяти, что в общем-то "терпимо".
Вот .. так как-то. Универсального решения нет и в этом направлении. :)