Macros for DDR and PORT from pin name?

Go To Last Post
30 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: 1

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);
    }
}

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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: 1
      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.

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

Hi,

I know this is old thread, but very useful thread, thanks to every contributors. 

 

This link is not working:

heinrichs.hj wrote:
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

 

May be this the same link:

 

https://community.atmel.com/proj...

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

Sure looks like it.

 

BTW you can turn an "old link" into usuable by changing www.avrfreaks to legacy.avrfreaks. Doing that I get:

 

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

This legacy site requires login: 

clawson wrote:

Sure looks like it.

 

BTW you can turn an "old link" into usuable by changing www.avrfreaks to legacy.avrfreaks. Doing that I get:

 

Thanks clawson for the reply. You were the first guy who helped me out for my first "volatile" variables problem. I thank you for that. 

 

Now I tried to run this:

heinrichs.hj wrote:
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

 

in my Atmega128A board and AS7.

 

Most things are working fine as per given in main.c. But only gpio_toggle is not working. My main.c is as follows: 

#define F_CPU        7372800               		// 7.37MHz processor
#include <avr/io.h>
#include "gpio.h"
#include <util/delay.h>

//         S         P    M    L
//         Y         O    S    S
//         M         R    B    B
//         B         T
//         O
//         L
//-------------------------------
#define    LED_cfg   A,   0,   0

int main(void)
{
	//
	//  The simple case -
	//  a single output bit.
	//  E.g. using LED_cfg.
	//
	gpio_conf( LED_cfg, OUTPUT, 0 );
	gpio_wr( PORT, LED_cfg, 1 );	//this is working fine
	_delay_ms(5000);
	
	gpio_set( LED_cfg );			//this is working fine
	_delay_ms(5000);
	
	gpio_clr( LED_cfg );			//this is working fine
	_delay_ms(5000);
	
	while(1)
	{
		gpio_toggle( LED_cfg );		//this is not working.
		_delay_ms(500);
	}
}

pardon my ignorance, as I have been off the greed for long time. 

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

I didn't look at that header file, but it's possible gpio_toggle is implemented by writing a 1 to the respective bit in the PINx register. I think the 128A doesn't have this feature.

 

But it's easy to check, post the assembly output (using avr-objdump -d on the output .elf file).

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

El Tangas wrote:

I didn't look at that header file, 

 

Yes you are right it is writing 1 to PINx reg. This is code of gpio_toggle in gpio.h is: 

 

/* ------------------------------------------------------------------
 *
 *  g p i o _ t o g g l e (cfg)
 *
 *  Toggles this GPIO by writing a '1' to all corresponding bits
 *  in the PIN register.
 *
 *  Return      : None
 *  Parameters  : cfg - Symbolic name of entry in config. table
 * ------------------------------------------------------------------
 */
#define gpio_toggle(cfg)                                            \
({                                                                  \
    if( ARG_nbits(cfg)==1 )                                         \
        ARG_pin(cfg)|=ARG_mask(cfg);                                \
    else                                                            \
        ARG_pin(cfg)=ARG_mask(cfg);                                 \
})

/* =============================================================== */
/*  Internally required macros - not for user access.              */
/* =============================================================== */
#define ARG_port(P,...)         (PORT ## P)
#define ARG_ddr(P,...)          (DDR  ## P)
#define ARG_pin(P,...)          (PIN  ## P)
#define ARG_lsb(P,msb,lsb)      (lsb)
#define ARG_reg(reg,P,... )     (reg ## P)
#define ARG_nbits(P,msb,lsb)    ((msb)-(lsb)+1)

/*
 *  The macro below creates a bitmask with '1' bits, beginnning from
 *  msb downto lsb. E.g. ARG_mask(D,4,1) creates the value 0x1E.
 */
#define ARG_mask(P,msb,lsb)     (((1<<((msb)+1))-1)&~((1<<(lsb))-1))

 

I have tried this also:

 

#define gpio_toggle(cfg)                                            \
({                                                                  \
	ARG_port(cfg)^=ARG_mask(cfg);				\
})

but no success.

 

El Tangas wrote:

But it's easy to check, post the assembly output (using avr-objdump -d on the output .elf file).

 

I don't know where to put this flags so tried in simulator and disassembly window. But for both the cases it shows:

 

		gpio_toggle( LED_cfg );		//this is not working.
0000013D  SBI 0x19,0		Set bit in I/O register 

 

which is I think writing to PINA0, isn't it?

 

 

 

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

Yes, that's the I/O address of PINA on the Mega128A. That toggle function doesn't work on this chip, not all megas have the same capabilities.

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

But why even this :

 

#define gpio_toggle(cfg)                                            \
({                                                                  \
	ARG_port(cfg)^=ARG_mask(cfg);				\
})

is not working?

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

Build the code with -save-temps and look at the .i file

 

That will let you see what the macros are actually expanding out to be.

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

clawson wrote:

Build the code with -save-temps and look at the .i file

 

Ok. I don't know how to set this flag but I found this in project properties:

 

 

And I found my main.i file in <SolutionDir>/<ProjectDir>/Debug/main.i.

 

In both the cases with orginal code in .h file, and modified "ARG_port(cfg)^=ARG_mask(cfg);" this main.i file remains same. See portion of toggle while loop:

 

 while(1)
 {
  ({ if( ((0)-(0)+1)==1 ) (
# 34 ".././main.c" 3
 (*(volatile uint8_t *)((0x19) + 0x20))
# 34 ".././main.c"
 )|=(((1<<((0)+1))-1)&~((1<<(0))-1)); else (
# 34 ".././main.c" 3
 (*(volatile uint8_t *)((0x19) + 0x20))
# 34 ".././main.c"
 )=(((1<<((0)+1))-1)&~((1<<(0))-1)); });
  _delay_ms(500);
 }
}

 

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

My problem got solved, I am really sorry for wasting your time. Changing the file gpio.h and function gpio_toggle with ARG_port(cfg)^=ARG_mask(cfg); worked. 

 

So, here is what happened.

 

I normally store common code files that could be useful for many projects, outside the project directory. I saved this gpio.h in common code files location. I normally add such files via "Add As links" in whichever projects I need them. But today when I started this project, I have clicked "Add files" instead, which physically copied gpio.h into my project folder. When I noticed that, I simply removed it from Solution explorer but forgot to delete from the project folder. Then I again added the original gpio.h file from that common code files location, and for all the time I edited it, but the project build process actually using the physical copy stored in my project folder location, which was never edited. Thanks to clawson and El Tangas I have gone through disassembler and temp files for multiple times, only to find that it is not changing no matter what I change in my "Linked" gpio.h. So I think that there may be other gpio.h. So I removed all gpio.h links from my project/solution, and right clicked "gpio_toggle" > Goto Implementation, which gave me the file stored in my project directory. I deleted that file and again linked the gpio.h file from my common code directory, with gpio_toggle ==== ARG_port(cfg)^=ARG_mask(cfg); and success. 

 

Thanks again to all the contributors and sorry again.

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

This legacy site requires login: 

Only for content that requires it.  The projects section is one of them.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]