/*
 * Arduino Port Manipulation (Registers Access)
 * by Carlo Carrano
 * from the idea of Khaled Magdy
 *
 * See:
 * 	https://deepbluembedded.com/arduino-port-manipulation-registers-example/
 *
 * Can be used with any Arduino sporting the ATMega328 chip microcontroller
 *
 *
 * Ports grounp together a number of Arduino pins, as follows
 * 	port		ATMega328p pin			Arduino pin
 * -------------------------------------------------
 *    D				PD0						D0
 * 					PD1						D1
 * 					PD2						D2
 * 					PD3						D3
 * 					PD4						D4
 * 					PD5						D5
 * 					PD6						D6
 * 					PD7						D7
 * -------------------------------------------------
 * 	  B				PB0						D8
 * 					PB1						D9
 * 					PB2						D10
 * 					PB3						D11
 * 					PB4						D12
 * 					PB5						D13
 * -------------------------------------------------
 * 	  C				PC0						A0
 * 					PC1						A1
 * 					PC2						A2
 * 					PC3						A3
 * 					PC4						A4
 * 					PC5						A5
 *
 *	- 3 DDR registers, one for each I/O port: DDRB, DDRC, DDRD
 * 	  Used to set the mode (input or output) for a specific I/O pin or a complete port of pins
 * 	  (see table above)
 *
 * 	- 3 PORT registers, one for each I/O port: PORTB, PORTC, PORTD
 * 	  Used to write to a specific port pin or to a complete port of pins
 *
 * 	- 3 PIN registers, one for each I/O port: PINB, PINC, PIND
 * 	  Used to get the port/pin state independently of the mode being selected
 */

#ifndef _port_manipulation_h_
#define _port_manipulation_h_

// all port pins output
// example:		SET_ALL_OUTPUTS(B);  // Set all pins on port B as output pins
#define SET_ALL_OUTPUTS(port) (DDR ## port= 0xFF)

// all port pins input
// example:		SET_ALL_INPUTS(C);  // Set all pins on port C as input pins
#define SET_ALL_INPUTS(port) (DDR ## port = 0x00)

// set port mode
// example:		SET_PORT_MODE(B, 0x03);   // Sets Pin 0 and 1 As Outputs. And Pins(2 to 5) As Input
#define SET_PORT_MODE(port, ddr_cfg) (DDR ## port = ddr_cfg)

// port set high
// example:		PORT_SET_HIGH(B);  // Sets all PORTB pins to a High state
#define PORT_SET_HIGH(port) (PORT ## port= 0xFF)

// port set low
// example:		PORT_SET_LOW(B);  // Sets all PORTB pins to a LOW state
#define PORT_SET_LOW(port) (PORT ## port= 0x00)

// port toggle
// example:		PORT_TOGGLE(B);  // Toggle all PORTB pins' states
#define PORT_TOGGLE(port) ((PORT ## port) = ~(PORT ## port))

// port write
// example:		PORT_WRITE(B, 0x0F);  // Writes to PORTB pins (pin0 to pin3 are HIGH) and (pin4 & pin5 are LOW)
#define PORT_WRITE(port, data) (PORT ## port= data)

// port read
// example:		uint8_t PORTB_State = PORT_READ(B);  // Read the state of all PORTB pins
#define PORT_READ(port) PIN ## port

// pin set mode
// example:		SET_PIN_MODE(B, 5, OUTPUT);  // Sets Pin13 (B5) As Output Pin
#define SET_PIN_MODE(port, pin, mode) \
if (mode == INPUT) { \
  DDR ## port &= ~(1 << pin); \
} else if (mode == OUTPUT) { \
  DDR ## port |= (1 << pin); \
}
// pin set mode alternative macros
#define SET_PIN_MODE_OUTPUT(port, pin) DDR ## port |= (1 << pin)
#define SET_PIN_MODE_INPUT(port, pin) DDR ## port &= ~(1 << pin)

// pin set high
// example:		SET_PIN_HIGH(B, 5);   // Sets Pin13 (B5) To Be HIGH
#define SET_PIN_HIGH(port, pin) (PORT ## port |= (1 << pin))

// pin set low
// example:		SET_PIN_LOW(B, 5);   // Sets Pin13 (B5) To Be LOW
#define SET_PIN_LOW(port, pin) ((PORT ## port) &= ~(1 << (pin)))

// pin toggle
// example:		PIN_TOGGLE(B, 5);   // Toggle Pin13 (B5) State
#define PIN_TOGGLE(port, pin) (PORT ## port) ^= (1 << (pin))

// pin read, to read the state of a specific pin regardless its mode
// example:		uint8_t btnState = PIN_READ(D, 4);   // Read The Arduino Pin4 (PD4) Digital State
#define PIN_READ(port, pin) (PIN ## port & (1 << pin))




#endif
