machines/components/pid-controller/firmware/common/Pin.h
2024-01-20 15:04:46 +01:00

512 lines
9.8 KiB
C++

#ifndef PIN_H
#define PIN_H
// this class doesn't outperform fastgpio but is works with AtmelMega
#include <Arduino.h>
#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