#ifndef PIN_H #define PIN_H // this class doesn't outperform fastgpio but is works with AtmelMega #include #define DDR_HIGH (*_DDR |= _offset) ///< Set the DDR register to HIGH for the pin #define DDR_TOGGLE (*_DDR ^= _offset) ///< Set the DDR register to the inverse for the pin #define DDR_LOW (*_DDR &= _ioffset) ///< Set the DDR register to LOW for the pin #define PORT_HIGH (*_PORT |= _offset) ///< Set the PORT register to HIGH for the pin #define PORT_TOGGLE (*_PORT ^= _offset) ///< Set the PORT register to the inverse for the pin #define PORT_LOW (*_PORT &= _ioffset) ///< Set the PORT register to LOW for the pin #define DDR_ON (*_DDR & _offset) ///< Get the DDR register for the pin (HIGH, LOW) with other pins forced to 0 #define DDR_OFF (*_DDR | _ioffset) ///< Get the DDR register for the pin (HIGH, LOW) with other pins forced to 1 #define PORT_ON (*_PORT & _offset) ///< Get the PORT register for the pin (HIGH, LOW) with other pins forced to 0 #define PORT_OFF (*_PORT | _ioffset) ///< Get the PORT register for the pin (HIGH, LOW) with other pins forced to 1 #define PIN_ON (*_PIN & _offset) ///< Get the PIN register for the pin (HIGH, LOW) with other pins forced to 0 #define PIN_OFF (*_PIN | _ioffset) ///< Get the PIN register for the pin (HIGH, LOW) with other pins forced to 1 class Pin { public: Pin(uint8_t number) { _number = number; _offset = digitalPinToBitMask(_number); _ioffset = ~_offset; _timer = digitalPinToTimer(_number); _PIN = portInputRegister(digitalPinToPort(_number)); _PORT = portOutputRegister(digitalPinToPort(_number)); _DDR = portModeRegister(digitalPinToPort(_number)); } /** Custom board constructor getAnalogValue() and setDutyCycle(int value) not supported @param number pin number written on board @param offset bit mask used to access pin in registers @param timer timer for pin @param PIN input register for pin @param PORT data register for pin @param DDR data direction register for pin */ Pin(uint8_t number, uint8_t offset, uint8_t timer, volatile uint8_t *PIN, volatile uint8_t *PORT, volatile uint8_t *DDR) { _number = number; _offset = offset; _ioffset = ~_offset; _timer = timer; _PIN = PIN; _PORT = PORT; _DDR = DDR; } // ################################# Operators ################################# /** Get the value of the pin from the PIN register @return true if the value of the pin is HIGH, false otherwise */ operator bool() const { return bool(PIN_ON); } /** Set the pin state @param state the state of the pin (HIGH, LOW) */ Pin &operator=(uint8_t state) { uint8_t oldSREG = SREG; cli(); if (state == LOW) { PORT_LOW; } else { PORT_HIGH; } SREG = oldSREG; return *this; } // ################################# Getters ################################# /** Get the pin number @return pin number */ uint8_t getNumber() { return _number; } /** Get the pin offset @return pin offset */ uint8_t getOffset() { return _offset; } /** Get the inverse pin offset @return inverse pin offset */ uint8_t getInverseOffset() { return _ioffset; } /** Get the pin timer @return pin timer */ uint8_t getTimer() { return _timer; } /** Get a pointer to the PIN register @return pointer to the PIN register */ volatile uint8_t *getPIN() { return _PIN; } /** Get a pointer to the PORT register @return pointer to the PORT register */ volatile uint8_t *getPORT() { return _PORT; } /** Get a pointer to the DDR register @return pointer to the DDR register */ volatile uint8_t *getDDR() { return _DDR; } /** Get the mode of the pin from the DDR register @return mode of the pin (OUTPUT, INPUT) */ uint8_t getMode() { if (DDR_ON) { return OUTPUT; } else { return INPUT; } } /** Get the state of the pin from the PORT register @return state of the pin (HIGH, LOW) */ uint8_t getState() { if (PORT_ON) { return HIGH; } else { return LOW; } } /** Get the value of the pin from the PIN register @return value of the pin (HIGH, LOW) */ uint8_t getValue() { if (PIN_ON) { return HIGH; } else { return LOW; } } /** Get the analog value of the pin @return analog value of the pin (0-1023) */ uint16_t getAnalogValue() { return analogRead(_number); } /** Set the pin mode and pin state @param mode the mode of the pin (OUTPUT, INPUT) @param state the state of the pin (HIGH, LOW) */ void set(uint8_t mode, uint8_t state) { uint8_t oldSREG = SREG; cli(); if (mode == INPUT) { DDR_LOW; } else { DDR_HIGH; } if (state == LOW) { PORT_LOW; } else { PORT_HIGH; } SREG = oldSREG; } /** Set the pin mode @param mode the mode of the pin (OUTPUT, INPUT) */ void setMode(uint8_t mode) { uint8_t oldSREG = SREG; cli(); if (mode == INPUT) { DDR_LOW; } else { DDR_HIGH; } SREG = oldSREG; } /** Set the pin state @param state the state of the pin (HIGH, LOW) */ void setState(uint8_t state) { uint8_t oldSREG = SREG; cli(); if (state == LOW) { PORT_LOW; } else { PORT_HIGH; } SREG = oldSREG; } // #################### Input #################### void setInput() { uint8_t oldSREG = SREG; cli(); DDR_LOW; SREG = oldSREG; } /** Set the pin pullup resistor to on */ void setPullupOn() { uint8_t oldSREG = SREG; cli(); PORT_HIGH; SREG = oldSREG; } /** Set the pin pullup resistor to off */ void setPullupOff() { uint8_t oldSREG = SREG; cli(); PORT_LOW; SREG = oldSREG; } /** Set the pin mode to input and the pin pullup resistor to on */ void setInputPullupOn() { uint8_t oldSREG = SREG; cli(); DDR_LOW; PORT_HIGH; SREG = oldSREG; } /** Set the pin mode to input and the pin pullup resistor to off */ void setInputPullupOff() { uint8_t oldSREG = SREG; cli(); DDR_LOW; PORT_LOW; SREG = oldSREG; } // #################### Output #################### /** Set the pin mode to output */ void setOutput() { uint8_t oldSREG = SREG; cli(); DDR_HIGH; SREG = oldSREG; } /** Set the pin output to HIGH */ void setHigh() { uint8_t oldSREG = SREG; cli(); PORT_HIGH; SREG = oldSREG; } /** Set the pin output to LOW */ void setLow() { uint8_t oldSREG = SREG; cli(); PORT_LOW; SREG = oldSREG; } /** Set the pin mode to output and the pin output to HIGH */ void setOutputHigh() { uint8_t oldSREG = SREG; cli(); DDR_HIGH; PORT_HIGH; SREG = oldSREG; } /** Set the pin mode to output and the pin output to LOW */ void setOutputLow() { uint8_t oldSREG = SREG; cli(); DDR_HIGH; PORT_LOW; SREG = oldSREG; } /** Set the PWM duty cycle @param value the duty cycle (0-255) */ void setDutyCycle(int value) { analogWrite(_number, value); } // ################################# Utilities ################################# /** Toggle the pin mode (OUTPUT -> INPUT, INPUT -> OUTPUT) */ void toggleMode() { uint8_t oldSREG = SREG; cli(); DDR_TOGGLE; SREG = oldSREG; } /** Toggle the pin state (HIGH -> LOW, LOW -> HIGH) */ void toggleState() { uint8_t oldSREG = SREG; cli(); PORT_TOGGLE; SREG = oldSREG; } // #################### RC Timer #################### /** Set the pin mode to input and decrement a counter until the pin goes HIGH or the counter reaches 0 then set the pin mode to output and return the counter value @param count the initial value for the counter to start at (0-65535) @return the value remaining on the counter when the pin state went to HIGH or 0 if the counter reached 0 */ volatile unsigned int rcTimer(volatile unsigned int count) { uint8_t status; asm volatile( // Save interupt status and disable interupts "in %[status], __SREG__ \n\t" // Store current interupt status in variable 's' "cli \n\t" // Disable interupts // Set Pin to input mode to start charging capacitor "ld __tmp_reg__, %a[_DDR] \n\t" // Load the DDR register into r0 (__tmp_reg__) "and __tmp_reg__, %[_ioffset] \n\t" // Apply the bit mask (offset) to r0 (__tmp_reg__) "st %a[_DDR], __tmp_reg__ \n\t" // Store r0 (__tmp_reg__) in the DDR register // Count time before Pin becomes high "loop%=: \n\t" // Label for looping //"ld __tmp_reg__,%a[_PIN] \n\t" // Load the PIN register into r0 (__tmp_reg__) "and __tmp_reg__, %[_offset] \n\t" // Apply the bit mask (offset) to r0 (__tmp_reg__) "brne end%= \n\t" // End the loop if r0 (__tmp_reg__) is not equal to zero by branching to label 'end' "dec %[count] \n\t" // Decrement the value of 'count' by one "brne loop%= \n\t" // If the value of 'count' is not equal to zero continue the loop by branching to label 'loop' // Done counting "end%=: \n\t" // Label for ending loop // Set Pin to output mode to start discharging capacitor "ld __tmp_reg__, %a[_DDR] \n\t" // Load the DDR register into r0 (__tmp_reg__) "or __tmp_reg__, %[_offset] \n\t" // Apply the bit mask (offset) to r0 (__tmp_reg__) "st %a[_DDR], __tmp_reg__ \n\t" // Store r0 (__tmp_reg__) in the PORT register // Restore interupt status "out __SREG__, %[status] \n\t" // Load interupt status from variable 's' // Outputs : [ count ] "+r"(count), // The value the counter was at when the pin went high [ status ] "=&r"(status) // The interupt status // Inputs : [ _DDR ] "e"(_DDR), // The address of the DDR register for the pin [ _PIN ] "e"(_PIN), // The address of the PIN register for the pin [ _offset ] "r"(_offset), // The bit mask used to access pin in registers [ _ioffset ] "r"(_ioffset) // The inverse bit mask used to access pin in registers ); return count; } private: uint8_t _number; uint8_t _offset; uint8_t _ioffset; uint8_t _timer; volatile uint8_t *_PIN; volatile uint8_t *_PORT; volatile uint8_t *_DDR; }; #endif