[CODE] [C] Simplifying PORT and DDR #defines for Portability

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

My first tutorial:

A philosophy of mine for programming is eliminating repeated code as much as possible. Because of this I've always been "annoyed" at code like this for use in generalized functions:

// Defines like this
#define LED_PORT PORTB
#define LED_DDR  DDRB
#define LED_PIN  PINB
#define LED_BIT  1

// To make this code more general
LED_DDR  |= 1<<LED_BIT; // Set output
LED_PORT |= 1<<LED_BIT; // Turn on
LED_PIN  |= 1<<LED_BIT; // Toggle

This may not look repetitive, but I wanted to make it look like this using a macro:

#define LED_PORT B
#define LED_PIN  1

DDR(LED_PORT)  |= 1<<LED_PIN;
PORT(LED_PORT) |= 1<<LED_PIN;
PIN(LED_PORT)  |= 1<<LED_PIN;

This simplifies the defines that need to be adjusted for different boards but makes the actual code a little different.

If anyone else has tried to do this, the knee-jerk is to use these macros:

#define PORT(port) PORT ## port
#define DDR(port)  DDR  ## port
#define PIN(port)  PIN  ## port

Unfortunately, this does not work. You get errors saying something to the effect of PORTLED_PORT undefined that will force most to give up and use the standard of a separate define for PORT, DDR, and PIN. What happens is the compiler actually appends the literal LED_PORT to PORT instead of taking the value we defined LED_PORT to be.

Fortunately, there is a workaround for this. You just need an extra macro step:

#define PORT_(port) PORT ## port
#define DDR_(port)  DDR  ## port
#define PIN_(port)  PIN  ## port

#define PORT(port) PORT_(port)
#define DDR(port)  DDR_(port)
#define PIN(port)  PIN_(port)

I hope someone likes & uses this!

Pushing AVRs to their limits

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

hi, i got a nice code to define i/o port. The author is Tomasz Ostrowski so all credit belong to this man.

// define electric connections according to your circuit, DATA line
#define DATA_PORT		PORTB
#define DATA_DDR		DDRB
#define DATA_PINPORT	PINB
#define DATA_PIN		0
#define SWITCH_DATA_IN  DATA_DDR &= ~_BV(DATA_PIN)
#define SWITCH_DATA_OUT DATA_DDR |= _BV(DATA_PIN); NOP	///< \todo is NOP needed?
#define CLEAR_DATA      DATA_PORT &= ~_BV(DATA_PIN)
#define SET_DATA        DATA_PORT |= _BV(DATA_PIN)
#define DATA            (DATA_PINPORT & _BV(DATA_PIN))

and i add 'toggle' function to the code

#define TOGGLE_DATA        DATA_PORT ^= _BV(DATA_PIN)

i usually write this in ionames.h then i included this file to my main program.

hope you like it.

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

hey cinderblock,

very very clean code, I only wish I knew what your macros are actually doing, your code makes the most sense... once you get to use it in your code.

Thanks for your help.

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

Quote:
You just need an extra macro step

Warning: there are a bunch of things in the area of macro expansion and stringification and concatenation and the performance order thereof that are NOT DEFINED in the C standard, and therefore subject to change when you switch compilers or versions of a compiler...

In fact, we had a grand old time when the behavior changed between gcc 3.4.5 and 4.1 (or something like that.)

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

Now I've always wondered why we don't define our bits this way:

#define BLUE_LED 1
#define RED_LED  2 
#define GREEN_LED 4

So we could just set:

DDRB = BLUE_LED | RED_LED | GREEN_LED

274,207,281-1 The largest known Mersenne Prime

Measure twice, cry, go back to the hardware store

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define BLUE_LED_bm 1
#define RED_LED_bm  2
#define GREEN_LED_bm 4

So we could just set:

DDRB = BLUE_LED_bm | RED_LED_bm | GREEN_LED_bm

It would be far more natural to use bit_masks instead of bit_positions. However the Atmel data sheets chose bit_positions.

I have always assumed that this was because some ASM ops use bit_positions e.g.

    SBRS   r16,3 

There are many different schemes for describing special_bits in special function registers. Personally, I like the bitfield_structure used by PIC (and others).

It is a historic thing. I suspect that the early compilers were not clever enough. Hence we are stuck with the bit_position scheme.

David.

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

Quote:

Now I've always wondered why we don't define our bits this way:

Funny I gave the answer to this very question yesterday.

http://www.avrfreaks.net/index.p...

Nothing stops you defining bit masks rather than bit numbers if you prefer. In fact if you are willing to live without io.h (or whatever it's called in your compiler) you can redefine the entire set of SFRs and bit names if you like except that it would hugely confuse anyone else trying to use the code if they assumed registers/bits defined the "normal" way. One possibility is to use a case change to avoid name pollution such as:

#define Txen 0x08
#define Rxen 0x10

UCSRB = Txen | Rxen;

Another alternative is:

typedef struct {
 uint8_t Txb8:1;
 uint8_t Rxb8:1;
 uint8_t Ucsz2:1;
 uint8_t Txen:1;
 uint8_t Rxen:1;
 uint8_t Udrie:1;
 uint8_t Txcie:1;
 uint8_t Rxcie:1;
} UB_t;

#define Ucsrb (*(volatile UB_t *)0x2A)

Ucsrb.Txen = 1;

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

Hey guys,

I'm a bit of a C noob, but this seems like the correct thread. Any chance you could help me out here a bit? Currently I'm doing this in my code:

#define LED0 PA0 // assign LED names to output ports on the attiny84
#define LED1 PA1
#define LED2 PA2
#define LED3 PA3
#define LED4 PA4
#define LED5 PA5
#define LED6 PA6
 
#define LED0_CLEAR (pin_level &= ~(1 << LED0))
#define LED1_CLEAR (pin_level &= ~(1 << LED1))
#define LED2_CLEAR (pin_level &= ~(1 << LED2))
#define LED3_CLEAR (pin_level &= ~(1 << LED3))
#define LED4_CLEAR (pin_level &= ~(1 << LED4))
#define LED5_CLEAR (pin_level &= ~(1 << LED5))
#define LED6_CLEAR (pin_level &= ~(1 << LED6))
 
// Set bits corresponding to pin usage above
#define PORT_MASK  (1 << PA0)|(1 << PA1)|(1 << PA2)|(1 << PA3)|(1 << PA4)|(1 << PA5)|(1 << PA6)

It seems silly to me, can you guys recommend a way to condense this? Note: This is for a led fader project, and the amount of LEDs is variable (based on chip selection and well, how many LEDs you want to hook up) so I would like to write code that based on a CHMAX variable will automatically set things up correctly.

Thanks in advance for your advice and help!

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

Note that the XMEGA and UC3 device headers *do* contain bit masks as well as (in some cases) bit positions. They also contain group masks, so code for them usually looks quite clean.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

Any chance you could help me out here a bit?

What is "pin_level" in the code you show?

(BTW not entirely sure a tutorial thread is the right place for this - usually the following thread is just to suggest improvements to the tutorial).

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

Hmm. OK I will re post to another thread. Sorry folks!

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

Thanks for the tutorial, I got some ideas as to how this can be extended.

What I did was combine your method, with how I reference bitmask and ports,
e.g. for PA1:

//## PORT:A
    //PA1
    #define BITMASK_PA1    _BV(PA1)
    #define PORT_PA1       PORTA
    #define DDR_PA1        DDRA
    #define PIN_PA1        PINA

The preprocessor snippet for setting PORT register:

// These accept letter + number reference only.
//      e.g. for PA5 -> SET_PORT(A5,0xFF)
// ( R_ was a cinderblock suggestion, to allow for nested concat operations)
#define R_SET_PORT(PIN,VALUE)PORT_P##PIN = ((BITMASK_P##PIN & VALUE) | (PORT_P##PIN & ~BITMASK_P##PIN))

#define SET_PORT(PIN,VALUE)     R_SET_PORT(PIN,VALUE)

The above combined together, allows for referencing pins like:

#define LED13 B5 // Only need to change this to swap pins

#define DDR_13  SET_DDR( B5, 0xFF ) 
//IN=0x00, OUT=0xFF
#define ON_13   SET_PORT( LED13, 0xFF )
#define OFF_13  SET_PORT( LED13, 0x00 ) 
//LOW=0x00, HIGH=0xFF

Where you only have to change macro PIN13 to change pins via a letter and a number representing port and pin. This removes the need to select a register and bitmask.

Hope this helps somebody. Any suggestions on making this more standardised welcomed.

Source code:http://pastebin.com/CE5WQ517

Perhaps, I should have made a version where instead of SET_DDR(A5,HIGH), its more like SET(DDR,A5,HIGH)? This can work, assuming its always going to be 'DDR' plus a letter for DDRA. Let me know what floats your boat, and I'll set it so it matches.

edit: source code clean up

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

My code below would be the best fit answer for the question and preferred for people who's already familiar with assembly bitwise codes.

#include <avr/io.h>

#define sbi(x,y) ((x) |= _BV(y))
#define cbi(x,y) ((x) &=~_BV(y))
#define tbi(x,y) ((x) ^= _BV(y))
#define gbi(x,y) ((x) & (1<< (y)))
#define vbi(x,y) (((x)>>(y))&1)

#define __DDR(pad) DDR ## pad
#define _DDR(pad) __DDR(pad)
#define DDR(name) _DDR(PAD_##name)

#define __PORT(pad) PORT ## pad
#define _PORT(pad) __PORT(pad)
#define PORT(name) _PORT(PAD_##name)

#define __PIN(pad) PIN ## pad
#define _PIN(pad) __PIN(pad)
#define PIN(name) _PIN(PAD_##name)

#define PAD(pad, name) _##pad(PAD_##name)

#define sbi_pad(pad, name) sbi(pad(name), PIN_##name)
#define cbi_pad(pad, name) cbi(pad(name), PIN_##name)
#define tbi_pad(pad, name) tbi(pad(name), PIN_##name)
#define gbi_pad(pad, name) gbi(pad(name), PIN_##name)
#define vbi_pad(pad, name) vbi(pad(name), PIN_##name)

Which will allow you to use it like these.

// good old bitwise actions
unsigned char flag = 0;
#define FLAG_HI 0
sbi(flag, FLAG_HI); //set flag
cbi(flag, FLAG_HI); //clear flag
tbi(flag, FLAG_HI); //toggle
gbi(flag, FLAG_HI); //get flag. Can be used on if clause like  if(gbi(flag, FLAG_HI)) {...}
vbi(flag, FLAG_HI); //makes it exactly 1 or 0. Useful on complex bit concatenation

// Suppose a red LED is connected on PD2
#define PAD_LED_R D
#define PIN_LED_R 2

sbi_pad(DDR, LED_R); // setup to output pin
...
sbi_pad(PORT, LED_R); // light on
// Also, below are the same as this
sbi(PORT(LED_R), PIN(LED_R));
sbi(PAD(PORT, LED_R), PIN(LED_R));

// When you need full pad access. Suppose there are 8 leds connected on PB0~PB7.
#define PAD_LEDS B
DDR(LED) = 0xFF; // setup to output pin
...
PORT(LED) = 0b11100100; // trivial multiple light on and off
#define PIN_LED_G 1
PORT(LED) = _BV(PIN(LED_R)) | _BV(PIN(LED_G)); // using _BV() from avr/io.h.
PORT(LED) = _BV(PIN_LED_R) | _BV(PIN_LED_G); // the same. choose on your favor

//which means you can also bitwisely access with variable index
char i;
for(i=3;i<=6;i++)
    sbi(PORT(LED), i); // turn on LED index 3 to 6

 

 

Last Edited: Thu. Jul 23, 2015 - 07:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys, 

I'm trying to do something like that but i got an error on my first define.
I'm trying to do a decode for a joystick and make it portable.
But when i do this

#define JOY B

#define JOY_DDR DDR(JOY)

...

...

I got an error on my first define. My Atmel Studio 6.2 gives me an error on my first define #define JOY B. It says 'B' undeclared (first use in this function) and points me to that line.

I've used it from an example on extreme electronics site from Avinash Gupta

Oh i almost forgot i have these include files
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

 

 

Thanks for any help

Last Edited: Wed. Oct 7, 2015 - 04:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Problem solved, thanks anyway

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

Problem solved, thanks anyway

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

How about the below implementation for IO port operations? Any performance issues? I used this for Atmega328P, but not sure of any side effects or bad programming practices!

enum
{
	CL_INPUT=0,
	CL_OUTPUT=1,
};

#define AVR_DIR_OUT 1
#define AVR_DIR_IN 0
#define IOPORTD 3
#define IOPORTB 1
#define IOPORTC 2

#define IOBitSet(ioport, bit) (ioport==IOPORTD)?(PORTD|=1<<bit):(ioport==IOPORTB)?(PORTB|=1<<bit):(ioport==IOPORTC)?(PORTC|=1<<bit):0
#define IOBitClear(ioport, bit) (ioport==IOPORTD)?(PORTD&=~(1<<bit)):(ioport==IOPORTB)?(PORTB&=~(1<<bit)):(ioport==IOPORTC)?(PORTC&=~(1<<bit)):0
#define IOBitDirOut(ioport, bit) (ioport==IOPORTD)?(DDRD|=1<<bit):(ioport==IOPORTB)?(DDRB|=1<<bit):(ioport==IOPORTC)?(DDRC|=1<<bit):0
#define IOBitDirIn(ioport, bit) (ioport==IOPORTD)?(DDRD&=~(1<<bit)):(ioport==IOPORTB)?(DDRB&=~(1<<bit)):(ioport==IOPORTC)?(DDRC&=~(1<<bit)):0
#define IOBitInvert(ioport, bit) (ioport==IOPORTD)?(PORTD^=1<<bit):(ioport==IOPORTB)?(PORTB^=1<<bit):(ioport==IOPORTC)?(PORTC^=1<<bit):0


// IS_BIT_SET returns non zero or zero value. So while using ensure you check if zero or non zero.
// Do not use for comparing with a value based on bit position.
#define IS_BIT_SET(ioport, bit) (ioport==IOPORTD)?(PIND&(1<<bit)):(ioport==IOPORTB)?(PINB&(1<<bit)):(ioport==IOPORTC)?(PINC&(1<<bit)):0


// Operation on Entire Ports
#define IOPortSet(ioport, value) (ioport==IOPORTD)?(PORTD|=(value)):(ioport==IOPORTB)?(PORTB|=(value)):(ioport==IOPORTC)?(PORTC|=(value)):0
#define IOPortClear(ioport, value) (ioport==IOPORTD)?(PORTD&=~(value)):(ioport==IOPORTB)?(PORTB&=~(value)):(ioport==IOPORTC)?(PORTC&=~(value)):0
#define IOPortDirOut(ioport, value) (ioport==IOPORTD)?(DDRD|=(value)):(ioport==IOPORTB)?(DDRB|=(value)):(ioport==IOPORTC)?(DDRC|=(value)):0
#define IOPortDirIn(ioport, value) (ioport==IOPORTD)?(DDRD&=~(value)):(ioport==IOPORTB)?(DDRB&=~(value)):(ioport==IOPORTC)?(DDRC&=~(value)):0
#define IOPortInvert(ioport, value) (ioport==IOPORTD)?(PORTD^=(value)):(ioport==IOPORTB)?(PORTB^=(value)):(ioport==IOPORTC)?(PORTC^=(value)):0
#define IOPortRead(ioport) (ioport==IOPORTD)?(PIND):(ioport==IOPORTB)?(PINB):(ioport==IOPORTC)?(PINC):0

// Only sample implementation. Mapping needs to be changed according to the hardware.

enum IODevices
{
	LED1=0,
	LED2,
	BUZZER,
	HCSR_04_TRIGGER1,
	HCSR_04_ECHO1,
        HCSR_04_TRIGGER2,
	HCSR_04_ECHO2,
	RELAY1,
	RELAY2,
};


struct IOStructure
{
	uint8_t port:2;
	uint8_t bitNum:3;
	uint8_t IOType:1;
};


// Update this structure when new devices have to be added.
// enum IODevices must also be updated// before updating this. Both must match in the order of assignment

struct IOStructure IODeviceList[] =
{
	{IOPORTD,PD3,CL_OUTPUT},               // LED1
	{IOPORTB,PB0,CL_OUTPUT},               // LED2
	{IOPORTB,PB1,CL_OUTPUT},			   // Pin for Buzzer
	{IOPORTD,PD3,CL_OUTPUT},               // Trigger1
	{IOPORTB,PB3,CL_INPUT},                // Echo1
        {IOPORTB,PB5,CL_OUTPUT},               // Trigger2
	{IOPORTB,PB4,CL_INPUT},                 // Echo2
	{IOPORTC,PC2,CL_OUTPUT},			   // Pin for Relay1
	{IOPORTC,PC3,CL_OUTPUT},			   // Pin for Relay2

	};


/*
// initializeAllIODevices function can be used if needed. But occupies little more memory.
// Since it is called only once, it is not required to go for this function. Instead all IOs
// can be initialized individually.
*/
void initializeAllIODevices (void)
{
	uint8_t count;
	for(count=0;count<(sizeof(IODeviceList)/sizeof(IODeviceList[0]));count++)
	{
		if(IODeviceList[count].IOType==CL_OUTPUT)
		{
			IOBitDirOut((IODeviceList[count].port),IODeviceList[count].bitNum);
		}
	}
}


void setIODeviceDir(enum IODevices device, uint8_t direction)
{
    if(direction==AVR_DIR_OUT)
    {
        IOBitDirOut(IODeviceList[device].port,IODeviceList[device].bitNum);
    }
    else
    {
        IOBitDirIn(IODeviceList[device].port,IODeviceList[device].bitNum);
    }

}

 // Function used to turn the IO pin connected to the device High/Low.
 // User need to take care of active high or active low configurations.
 // This function will just turn the port pin high
 void setIODeviceState (enum IODevices device, uint8_t state)
 {
	 if(state==HIGH)
     {
         IOBitSet(IODeviceList[device].port,IODeviceList[device].bitNum);
     }
     else
    {
        IOBitClear(IODeviceList[device].port,IODeviceList[device].bitNum);
    }
};


void toggleIODevice (enum IODevices device)
{
    IOBitInvert(IODeviceList[device].port,IODeviceList[device].bitNum);
};


   // Function used to check is the IO device connected to particular pin is High or low
   // This function will just check if the port pin High or not
   // In case the port pin is high, function will return non zero value
   // In case the IO pin is low, function will return false.
   // Please note that function will not return 1 in case bit is set.

 uint8_t isIODeviceHigh(enum IODevices device)
 {
	 return(IS_BIT_SET(IODeviceList[device].port,IODeviceList[device].bitNum));
};


/* pulseIODevice function is used to pulse the particular device for certain number
of times at a specific rate.
period is the total width of the pulse in milli seconds.
onTime: Ontime must be less than the rate
*/
void pulseIODevice(enum IODevices deviceNum, uint8_t numTimes, uint16_t period, uint16_t onTime)
{
	int loop;
	
	for(loop=0;loop<numTimes;loop++)
	{
		IOBitSet(IODeviceList[deviceNum].port,IODeviceList[deviceNum].bitNum);
				CL_delay_mS(onTime);
		IOBitClear(IODeviceList[deviceNum].port,IODeviceList[deviceNum].bitNum);
		CL_delay_mS(period-onTime);
	}

};

 

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

For microcontrollers the key thing is usually the size and speed of the code produced - how does that stack up?

 

(on cursory glance it looks very expensive - but maybe a lot of the code is optimized away?)

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

Yes, there is a significant performance issue after I tested. Wrote two routines. In first case, I see the pulse@2.667MHz and in second case the pulsing is @190.5KHz with 16MHz clock.

 //Case1: (Pl Comment when testing case 2)
    /*
    IOBitDirOut(IOPORTB,0);
    while(1)
    {
        IOBitSet(IOPORTB,0);
        IOBitClear(IOPORTB,0);
    }*/
    //Case2 (Pl Comment when testing case 1)
    setIODeviceDir(LED2,AVR_DIR_OUT);
    while(1)
    {
        setIODeviceState(LED2,HIGH);
        setIODeviceState(LED2,LOW);
    }

Another question I have is if I do not call any of the functions related to case2, compiler should optimize the code right? But I get approx 900 bytes of code when C files containing functions related to case2 are added to the project (but the functions are not called) against 140 bytes when C files related to case 2 above are removed from the project. I have optimization -Os selected. Any other settings? I am using the GNU compiler provided by Atmel.

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

joneewheelock wrote:
In first case, I see the pulse@2.667MHz
And what do you get for (the more obvious!):

DDRB |= (1 << 0);
while(1) {
    PORTB |= (1 << 0);
    PORTB &= ~(1 << 0);
}

joneewheelock wrote:
Another question I have is if I do not call any of the functions related to case2, compiler should optimize the code right?

If you build your code with -ffunction-sections on the compilation and -gc-sections on the link then, yes, any whole functions that are not called will not be linked into the final code image. From other threads I know you use Code::Blocks rather than an established IDE (AS7) or a Makefile so have you remembered to add those two options to your compile and link options? (you probably also want -fdata-sections on the compilation too).

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

joneewheelock wrote:
In first case, I see the pulse@2.667MHz and

When comparing performance of something like these i/o macro's its probably more informative to look at the list file.

 

Some time ago I had a breef peek into the "arduino" stuff and I bumped into some text somewhere about the terribly slow "digital_write()" stuff.

So then he took those functions and rewrote them as macro's. The author claimed his macro's were optimized by the compiler into single sbi() cbi() instructions.

(Euhhmmm.. The "normal" arduino digital functions check if a pin is used as pwm and turn of pwm if written to as a bit. I don't like arduino very much).

 

Well, lot's of people are complaining:

https://duckduckgo.com/html?q=ar...

 

This post drops "Paul Stoffregen" 's name. (& posts) He's a smart guy.

http://forum.arduino.cc/index.ph...

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

I modified my code written in previous post with only macros. This code is mainly for 328P. The code size and performance both seems to be optimized. But this code is not completely tested. Need to do some testing in next couple of days. Hopefully works straight. 

 

Just thought of using arduino compatible names (pinMode, digitalWrite and digitalRead) so that arduino code could be used directly.

 

But when I changed #define to static const uint8_t  in the above code (in commented section), I was expecting RAM usage to go up. I was thinking that new variable is created in RAM, but cannot be modified. That means static const is equivalent to #define as far as code size/performance is concerned and we could use one of them depending on our taste!

#undef HIGH
#define HIGH 1

#undef LOW
#define LOW 0


#undef OUTPUT
#define OUTPUT 1

#undef INPUT
#define INPUT 0

#ifndef IOPORTB
#define IOPORTB 0
#endif
#ifndef IOPORTC
#define IOPORTC 1
#endif
#ifndef IOPORTD
#define IOPORTD 2
#endif

#define IOPB0  0
#define IOPB1  1
#define IOPB2  2
#define IOPB3  3
#define IOPB4  4
#define IOPB5  5
#define IOPB6  6
#define IOPB7  7
#define IOPC0  8
#define IOPC1  9
#define IOPC2  10
#define IOPC3  11
#define IOPC4  12
#define IOPC5  13
#define IOPC6  14
#define IOPC7  15
#define IOPD0  16
#define IOPD1  17
#define IOPD2  18
#define IOPD3  19
#define IOPD4  20
#define IOPD5  21
#define IOPD6  22
#define IOPD7  23



/*
    static const uint8_t IOPB0=0;
    static const uint8_t IOPB1=1;
    static const uint8_t IOPB2=2;
    static const uint8_t IOPB3=3;
    static const uint8_t IOPB4=4;
    static const uint8_t IOPB5=5;
    static const uint8_t IOPB6=6;
    static const uint8_t IOPB7=7;
    static const uint8_t IOPC0=8;
    static const uint8_t IOPC1=9;
    static const uint8_t IOPC2=10;
    static const uint8_t IOPC3=11;
    static const uint8_t IOPC4=12;
    static const uint8_t IOPC5=13;
    static const uint8_t IOP6=14;
    static const uint8_t IOPC7=15;
    static const uint8_t IOPD0=16;
    static const uint8_t IOPD1=17;
    static const uint8_t IOPD2=18;
    static const uint8_t IOPD3=19;
    static const uint8_t IOPD4=20;
    static const uint8_t IOPD5=21;
    static const uint8_t IOPD6=22;
    static const uint8_t IOPD7=23;
*/

// Now define all IO devices and assign port numbers.
#define LED1 IOPD3
#define LED2 IOPB0


#define IOBitSet(ioport, bit) (ioport==IOPORTD)?(PORTD|=1<<bit):(ioport==IOPORTB)?(PORTB|=1<<bit):(ioport==IOPORTC)?(PORTC|=1<<bit):0
#define IOBitClear(ioport, bit) (ioport==IOPORTD)?(PORTD&=~(1<<bit)):(ioport==IOPORTB)?(PORTB&=~(1<<bit)):(ioport==IOPORTC)?(PORTC&=~(1<<bit)):0
#define IOBitDirOut(ioport, bit) (ioport==IOPORTD)?(DDRD|=1<<bit):(ioport==IOPORTB)?(DDRB|=1<<bit):(ioport==IOPORTC)?(DDRC|=1<<bit):0
#define IOBitDirIn(ioport, bit) (ioport==IOPORTD)?(DDRD&=~(1<<bit)):(ioport==IOPORTB)?(DDRB&=~(1<<bit)):(ioport==IOPORTC)?(DDRC&=~(1<<bit)):0
#define IOBitInvert(ioport, bit) (ioport==IOPORTD)?(PORTD^=1<<bit):(ioport==IOPORTB)?(PORTB^=1<<bit):(ioport==IOPORTC)?(PORTC^=1<<bit):0


// Operation on Entire Ports.
//Without affecting existing values, the new value is set or cleared for entire port.
// These are not tested yet.
#define portSet(ioport, value) (ioport==IOPORTD)?(PORTD|=(value)):(ioport==IOPORTB)?(PORTB|=(value)):(ioport==IOPORTC)?(PORTC|=(value)):0
#define portClear(ioport, value) (ioport==IOPORTD)?(PORTD&=~(value)):(ioport==IOPORTB)?(PORTB&=~(value)):(ioport==IOPORTC)?(PORTC&=~(value)):0
#define portDirOut(ioport, value) (ioport==IOPORTD)?(DDRD|=(value)):(ioport==IOPORTB)?(DDRB|=(value)):(ioport==IOPORTC)?(DDRC|=(value)):0
#define portDirIn(ioport, value) (ioport==IOPORTD)?(DDRD&=~(value)):(ioport==IOPORTB)?(DDRB&=~(value)):(ioport==IOPORTC)?(DDRC&=~(value)):0
#define portInvert(ioport, value) (ioport==IOPORTD)?(PORTD^=(value)):(ioport==IOPORTB)?(PORTB^=(value)):(ioport==IOPORTC)?(PORTC^=(value)):0

// Read the entire port and return the value read. Individual bits are not tested
#define portRead(ioport) (ioport==IOPORTD)?(PIND):(ioport==IOPORTB)?(PINB):(ioport==IOPORTC)?(PINC):0


// Wrote 3 arduino compatible functions, but uses just macros.
#define pinMode(pin,mode) ((mode==OUTPUT)?IOBitDirOut(((uint8_t)pin/8),(pin-((pin/8)*8))):IOBitDirIn(((uint8_t)pin/8),(pin-(pin/8)*8)))
#define digitalWrite(pin,value) ((value==LOW)?IOBitClear((pin/8),(pin-((pin/8)*8))):IOBitSet((pin/8),(pin-((pin/8)*8))))
#define digitalRead(pin) ((portRead(pin/8))&(1<<(pin-((pin/8)*8)))?HIGH:LOW)
// Extra function for inverting
#define digitalInvert(pin) (IOBitInvert((pin/8),(pin-((pin/8)*8))))

 

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

If you are talking about C++ then "static const" is preferable as it is type checked.

 

Still not entirely following how this equates to Arduino though? In Arduino I can "pinMode(13, OUTPUT); digitalWrite(13, HIGH)". I have no idea what "13" is. It could be PORTB bit 5 or it could be PORTD bit 2 or something completely different. I just know that will take the 13th pin on the header "high".

 

The way the Arduino code achieves this (even with different pin mapping on different Arduino layouts) is using look up tables that map "13" to a PORT, a DDR and a bit number. It's true this makes it hugely inefficient as there are multiple instances of indirection but it achieves the goal of mapping which makes things simple for the user.

 

How do your pinMode() and digitalWrite() achieve this mapping then?

 

If they need to know it is PORTB.5 then why don't they simply use:

DDRB |= (1 << 5);
PORTB |= (1 << 5);

or if they have an aversion to typing "1 <<" then:

DDRB |= _BV(5);
PORTB |= _BV(5);

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

From other threads I know you use Code::Blocks rather than an established IDE (AS7)

@clawson: I definitely want to use AS7 and you infact suggested earlier also. But unfortunately .Net+AS7 eats up my resources. Very slow in opening, compilation and many other problems we see such as with google chrome. Installation and un-installation is a nightmare. I think AS7 requires some kind of performance testing. Hopefully microchip will come out with better performance studio. We run Embedded Systems training center and our external trainers complain about the time that is getting wasted because of these factors. Codeblocks definitely has few issues but occupies very less resources and very fast. I will add options suggested by you or go for makefile (I need to learn how to use a make file). Even in other post you suggested to use -std=gnu99. I included, but -std=99 is also appearing even though I unchecked that option. Need to further look into it (avr-gcc.exe -Os -Wextra -Wall -mmcu=atmega328p -std=gnu99 -DF_CPU=16000000UL -Os -Wextra -Wall -Os -Wextra -Wall -std=c99  -c main.c -o Debug\main.o). My trainer is now configuring eclipse and hopefully that works out better.

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

Eclipse is a half way house between Code::Blocks (which is very simple) and AS7 (which is unnecessarily bloated). It should certainly let you control things like -std=

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

How do your pinMode() and digitalWrite() achieve this mapping then?

Mapping is done using

#define IOPB0  through #define IOPD7 and also with the help of #define LED1 IOPD3 , #define LED2 IOPB0 etc defined in the code above. I first get the port number using (pin/8) in 

#define digitalWrite(pin,value) ((value==LOW)?IOBitClear((pin/8),(pin-((pin/8)*8))):IOBitSet((pin/8),(pin-((pin/8)*8))))

Once I get the port number such as 0,1,2 etc, I use the macro IOBitSet() to set. I think I am convinced with this code and it should be working by just adding new input output devices. Let me completely test and see how this goes.

 

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

Just installed Eclipse Neon CDT and then added the AVR plugin from the "Marketplace" and when I create an AVR project I find I have this control:

 

 

In fact it picked -std=gnu99 as the default (which is nice!).

 

My only argument with this is that it doesn't seem to offer c89/gnu89 - but I guess no one wants those in this day and age?

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

Interesting. I'm not quite sure what I was expecting but under Codevision 'digitalWrite' is turned into SBI or CBI as appropriate.

 

Time to look at those macro expansions.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

I just posted that into Eclipse and used the line:

pinMode(LED1, OUTPUT);

Eclipse will show you the full expansion or it will allow you to step through the expansion 1 step at a time. Not that it matters (because it happens at compile time) but there are THIRTY-SEVEN steps in the expansion of that macro! The ultimate expansion is:

((1==1)?(((uint8_t)19/8)==2)?((*(volatile uint8_t *)((0x11) + 0x20))|=1<<(19-((19/8)*8))):(((uint8_t)19/8)==0)?((*(volatile uint8_t *)((0x17) + 0x20))|=1<<(19-((19/8)*8))):(((uint8_t)19/8)==1)?((*(volatile uint8_t *)((0x14) + 0x20))|=1<<(19-((19/8)*8))):0:(((uint8_t)19/8)==2)?((*(volatile uint8_t *)((0x11) + 0x20))&=~(1<<(19-(19/8)*8))):(((uint8_t)19/8)==0)?((*(volatile uint8_t *)((0x17) + 0x20))&=~(1<<(19-(19/8)*8))):(((uint8_t)19/8)==1)?((*(volatile uint8_t *)((0x14) + 0x20))&=~(1<<(19-(19/8)*8))):0)

Gulp! Quite clever the compiler boils all of that down into:

	pinMode(LED1, OUTPUT);
  6c:	8b 9a       	sbi	0x11, 3	; 17

 

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

Wow! That's interesting and good that it is compact. Even I ran performance test comparing with direct DDRX/PORTX usage and did not see any code/performance issue. I just need to see that macro side effect does not creep in and add appropriate parentheses (It is unlikely that expression is used. Or may be for looping through IO pins!!)

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

If you want to post any test code I'll happily run it through Codevision to test for compatibility.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

Attached is the simple code for water level controller for running it through Codevision. Small code occupying only 242 bytes ROM, 0 RAM.

Attachment(s): 

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

And Codevision's result using v3.23a...

 

174 words/348 bytes including interrupt vectors, delay_ms() and crt0.

 

List file...

 

Attachment(s): 

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

Joniwheelock, certainly when you say zero RAM, that must ignore ram usage by the stack. Local variables, as I recall, are put on the stack so are invisible as RAM use, but certainly do use it. And every function call uses stack which is RAM.

 

Best wishes!

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net