Macros for DDR and PORT from pin name?

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using C macros is it possible to extract the port information from the text of the pin name and use it to set the port and ddr. If it were possible to pull the "D" from the second character of PD1 you could use it to make PORTD out of PORT+character.

For example:

#define DDR(x) (something)
#define PORT(x) (something)
#define PIN(x) (something)

#define CLK PD0
#define SWITCH PB3

void foo()
{
  DDR( CLK) |= (1<<CLK);  // set clock pin for output
  DDR( SWITCH) &= ~(1<<SWITCH);; // set switch pin for input
  PORT( SWITCH) |= (1<<SWITCH);  // set switch pullup

  PORT( CLK) ^= (1<<CLK);  // toggle clock output

  if( PIN( SWITCH) & (1<<SWITCH))
  {
    // blah, blah
  }
}

Is something like that possible?

Cheers,

Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i guess it is not possible .. but you may call every port by a different name. and you may name the pins also.

like:

#define CLOCK PB0

void foo()
{
      DDRB = 0xFF;
      PORTB = (1 << CLOCK); /* or _BV(CLOCK); */
}

but i don't think that you ask about is possible.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I prefer to use these macros:

/***** Configure IO *****/
#define configure_as_input(bit)             {bit ## _DDR &= ~(1 << bit);}
#define configure_as_output(bit)            {bit ## _DDR |= (1 << bit);}

#define pullup_on(bit)                      {bit ## _PORT |= (1 << bit);}
#define pullup_off(bit)                     {bit ## _PORT &= ~(1 << bit);}

#define disable_digital_input(channel)      {DIDR0 |= (1 << channel);}
#define enable_digital_input(channel)       {DIDR0 &= ~(1 << channel);}

#define enable_pin_change_interrupt(pin)    {pin ## _PCMSK |= (1 << pin ## _PCINT);}
#define disable_pin_change_interrupt(pin)   {pin ## _PCMSK &= ~(1<< pin ## _PCINT);}


/***** Manipulate Outputs *****/
#define set_high(bit)                       {bit ## _PORT |= (1 << bit);}
#define set_low(bit)                        {bit ## _PORT &= ~(1 << bit);}
#define toggle(bit)                         {bit ## _PIN |= (1 << bit);}

/***** Test Inputs *****/
#define is_high(bit)                        (bit ## _PIN & (1 << bit))
#define is_low(bit)                         (! (bit ## _PIN & (1 << bit)))



/***** IO *****/
#define REED_CONTACT                PA0
#define REED_CONTACT_PORT           PORTA
#define REED_CONTACT_DDR            DDRA
#define REED_CONTACT_PIN            PINA
#define REED_CONTACT_PCMSK          PCMSK0
#define REED_CONTACT_PCINT          PCINT0


#define SWITCH                      PA1
#define SWITCH_PORT                 PORTA
#define SWITCH_DDR                  DDRA
#define SWITCH_PIN                  PINA
#define SWITCH_PCMSK                PCMSK0
#define SWITCH_PCINT                PCINT1


#define POT_ENABLE                  PA2
#define POT_ENABLE_PORT             PORTA
#define POT_ENABLE_DDR              DDRA
#define POT_ENABLE_PIN              PINA


#define POT_SIGNAL                  PA3
#define POT_SIGNAL_PORT             PORTA
#define POT_SIGNAL_DDR              DDRA
#define POT_SIGNAL_PIN              PINA


#define ISR_TEMP                    PB0
#define ISR_TEMP_PORT               PORTB
#define ISR_TEMP_DDR                DDRB
#define ISR_TEMP_PIN                PINB


#define PWM_OUT                     PB2
#define PWM_OUT_PORT                PORTB
#define PWM_OUT_DDR                 DDRB
#define PWM_OUT_PIN                 PINB

Then you can do:

// REED_CONTACT - Digital Input
enable_digital_input (REED_CONTACT);
enable_pin_change_interrupt (REED_CONTACT);

// SWITCH - Digital Input
enable_digital_input (SWITCH);
enable_pin_change_interrupt (SWITCH);

// POT_ENABLE - Digital Output
configure_as_output (POT_ENABLE);
set_low (POT_ENABLE);
enable_digital_input (POT_ENABLE);

// POT_SIGNAL - Analog Input
pullup_off (POT_SIGNAL);

// ISR_TEMP - DIGITAL OUTPUT
configure_as_output (ISR_TEMP);
set_low (ISR_TEMP);

// PWM_OUT - Digital Output
configure_as_output (PWM_OUT);
set_low (PWM_OUT);

if (is_high (REED_CONTACT) || is_low (SWITCH))
{
    do_something();
}

The expression set_low (PWM_OUT); is preproccessed to:
{PWM_OUT_PORT &= ~(1 << PWM_OUT);}
to
{PORTB &= ~(1 << PB2);}
to
... (Insert numbers and addresses here)

The ## directive combines two expressions.
tbl ## ough is combined to tblough

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you compare the register summary of several AVR datasheets you can see that the PORT DDR and PIN register are always located in the same order:

PORTx
DDRx
PINx

So you could define the following macros:

#define PIN(x) (*(&x - 2)) // Address Of Data Direction Register Of Port x 
#define DDR(x) (*(&x - 1)) // Address Of Input Register Of Port x
#define PORT(x) (x)

#define DATA PORTB

And then use the macros like this:

PORT (DATA) = 0xFF;
DDR (DATA) = 0;

if (PIN (DATA) & 0x80)
{
    do_something();
}

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Sebastian! That reminded me where I had see that before - in Peter Fluery's LCD code. Peter mentions in his comments that PORTF is a little different on the Mega64 and Mega128. I'll have to do a little checking to see if there are any other exceptions on the newer chips with large I/O counts.

/* 
** constants/macros 
*/
#define DDR( x) (*(&x - 1))	  /* address of data direction register of port x */
#if defined( __AVR_ATmega64__) || defined( __AVR_ATmega128__)
	/* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
	#define PIN( x) (&PORTF == &(x) ? _SFR_IO8( 0x00) : (*( &x - 2)))
#else
	#define PIN( x) ( *( &x - 2))	/* address of input register of port x		  */
#endif

That get's me down to having to just define a pin number and port for each I/O instead of having to define a DDRx, PINx, PORTx, and pin number.

Cheers,

Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Note that the toggle macro only works on the modern AVRs where writing to the PIN register toggles the corresponding PORT register.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another alternative, which works on the higher pin count avr's also

#include 

struct bits {
  uint8_t b0:1;
  uint8_t b1:1;
  uint8_t b2:1;
  uint8_t b3:1;
  uint8_t b4:1;
  uint8_t b5:1;
  uint8_t b6:1;
  uint8_t b7:1;
} __attribute__((__packed__)); 
#define BIT(name,pin,reg) \
 ((*(volatile struct bits*)®##name).b##pin) 

//define port letter and pin number
#define LED2(reg) BIT(D,5,reg)
#define SW2(reg) BIT(D,2,reg)

int main(){
    SW2(DDR)=0;
    SW2(PORT)=1; //pullup on
    LED2(DDR)=1;    
    while(1){
        if(SW2(PIN)==0){
            LED2(PORT)=1;
        }
        else{
            LED2(PORT)=0;
        }
    }
}

You can also make it more 'friendly' by adding these defines-

#define IN PIN
#define OUT PORT
#define DIR DDR

//SW2(DIR)=0;
//LED2(OUT)=1;
//if(SW2(IN)==0){
//etc.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could try these macros which I came across some time ago. I apologise to the original author, but I don't know who to give the credit to.


//********************************************************************
//          Macros for easy i/o pin access
//********************************************************************


#define BIT(p,b)                (b)

#define PORT(p,b)               (PORT ## p)
#define PIN(p,b)                (PIN ## p)
#define DDR(p,b)                (DDR ## p)


#define Set_Port_Bit(p,b)       ((p) |= _BV(b))
#define Clr_Port_Bit(p,b)       ((p) &= ~_BV(b))
#define Tgl_Port_Bit(p,b)       ((p) ^= _BV(b))

#define Get_Port_Bit(p,b)       (((p) & _BV(b)) != 0)


//user functions:
#define bit(io)	                BIT(io)
#define port(io)                PORT(io)

#define pin_high(io)            Set_Port_Bit(PORT(io),BIT(io))
#define pin_low(io)             Clr_Port_Bit(PORT(io),BIT(io))
#define pin_toggle(io)          Tgl_Port_Bit(PORT(io),BIT(io))

#define get_output(io)          Get_Port_Bit(PORT(io),BIT(io))
#define get_input(io)           Get_Port_Bit(PIN(io),BIT(io))

#define set_dir_in(io)          Clr_Port_Bit(DDR(io),BIT(io))
#define set_dir_out(io)         Set_Port_Bit(DDR(io),BIT(io))
#define dir_toggle(io)          Tgl_Port_Bit(DDR(io),BIT(io))

//********************************************************************
// define pins as:
// #define LED D,PD2
//
// write code as:
// pin_high(LED)
//
//********************************************************************


  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are defining function-style macros with two arguments, one not used, like

#define PORT(p,b)               (PORT ## p) 

But you are using them with a single argument only.

Stealing Proteus doesn't make you an engineer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S-Sohn thanks for macros! They are very useful.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ArnoldB wrote:
You are defining function-style macros with two arguments, one not used, like

#define PORT(p,b)               (PORT ## p) 

But you are using them with a single argument only.


And your point is....?
Work the macros through all the way, starting from one of the user functions to see how they work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Colin wrote:
And your point is....?
That this is shit.

Stealing Proteus doesn't make you an engineer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

what you trying to implement is some kind of hardware abstraction.
You could als have a look at this project in the projects section:
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=1036
These GPIO macros very efficiently implement this hardware abstraction layer.

Kind regards,
hj

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm comes closest to what I was looking for. With his macros, a single <#define LED2(reg) BIT(D,5,reg)> gives me access to the PORT, DDR, and PIN registers. Not as clean as just <#define LED2 PD5>, but much better than two or three defines for a single connection.

Cheers,

Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, every time I try to access a macro I get the following error: "expected identifier or '(' before '&' token"

I have followed one of the examples above and no matter how I "spin it" I keep on getting the same error. I am still a newbie but I think the following code should let me set ENCA_PIN as an input.

Here is the code I'm using. This is just a simple encoder + LCD application using a Mega328P and Peter Fluery's LCD code. And I am trying to write the encoder library in a similar fashion as Peter's library.

Thank you very much for any help you can provide.


#ifndef ENCODER_H
#define ENCODER_H

#include 
#include 

// Define I/O aliases
	#define ENCODER_PORT	PORTD				/**< encoder port   */
	#define ENCA_PORT	    ENCODER_PORT		/**< encoder A port */
	#define ENCB_PORT	    ENCODER_PORT		/**< encoder B port */
	#define ENC_Button_PORT ENCODER_PORT		/**< encoder Button port */
	#define ENCA_PIN		0					/**< encoder A pin  */
	#define ENCB_PIN		1					/**< encoder B pin  */
	#define ENC_Button_PIN  2					/**< encoder Button pin  */

#include 
#include 
#include 
#include "encoder.h"

/* 
** constants/macros 
*/
#define PORT(x) (x) 
#define DDR(x) (*(&x - 1)) // Address Of Input Register Of Port x
#define PIN(x) (*(&x - 2)) // Address Of Data Direction Register Of Port x


DDR(ENCA_PORT) |= (0 << ENCA_PIN);  //This is where I keep on getting the error
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
DDR(ENCA_PORT) |= (0 << ENCA_PIN);  //This is where I keep on getting the error 

Is that line within a function or not?

I reduced your code to a small one-source-file test program and it compiles w/o errors or warnings:

#include 

// Define I/O aliases
#define ENCODER_PORT   PORTD            /**< encoder port   */
#define ENCA_PORT       ENCODER_PORT      /**< encoder A port */
#define ENCA_PIN      0               /**< encoder A pin  */

#define DDR(x) (*(&x - 1)) // Address Of Input Register Of Port x

int main(void)
{
    while(1)
    {
      DDR(ENCA_PORT) |= (0 << ENCA_PIN);
    }
}

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you compile the code just as you have it, with the line hanging out in the middle of nowhere, you get the error that you state.

Of course the statement won't do anything since 0 shifted by anything is always 0.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
      DDR(ENCA_PORT) |= (0 << ENCA_PIN); 

Just to point out that this line is a "do nothing" command. Anything OR'd with 0 is unchanged. If the intention here was to ensure the ENCA_PIN bit is cleared to 0 then it should read:

      DDR(ENCA_PORT) &= ~(1 << ENCA_PIN); 

and if you don't understand why that is read the "Bit manipulation 101" thread in Tutorial Forum.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First of all thank you all for your responses, most appreciated.

JohanEkdahl hit the nail right on the head, I wasn't calling the macro from within a function. As soon as I created a function and wrapped around "DDR(ENCA_PORT) &= ~(1 << ENCA_PIN); " line it worked right away.

I'm sorry for the:

DDR(ENCA_PORT) |= (0 << ENCA_PIN);

:oops:
I was trying different scenarios before posting, including making the pin an output, eventually I just "fixed up" the code to post and didn't notice the mistake. I do understand (so I think) bitwise operations but still will take a look at the "Bit manipulation 101" forum, never know what I will learn.

Most of my roadblocks have more to do with syntax and rules in C than with the logic itself.

Once again thank you all for your time.