Question about MACRO expansion and substitution

I am not really sure how to phrase this question so an example may be better.

when accessing the timers the register names are defined as Macros in the form:
TCCR0A
TCCR0B
TCCR0C
....
TCCR5A
TCCR5B
TCCR5C
etc.

is it at all possible to define:
TCCRxA
TCCRxB
TCCRxC

and then substitute for x at compile time?
for example:

Timer_Clr(x)
{
TCCRxA = 0;
TCCRxB = 0;
TCCRxC = 0;
)

I have read through the documentation at: Macros (The C Preprocessor) but am new to using Macros and so may have missed it in the documentation, but nothing in there shows how it could be done.

Thanks
Chris

Then maybe an example is best. This is from w5100.h. I just modified it, so I knew where to find an example. It is a bit difficult to follow at first, but the defines replace __SOCKET_REGISTER8 with these functions.

#define __SOCKET_REGISTER8(name, address)                    \
  static inline void write##name(SOCKET _s, uint8_t _data) { \
    writeSn(_s, address, _data);                             \
  }                                                          \
  static inline uint8_t read##name(SOCKET _s) {              \
    return readSn(_s, address);                              \
  }

public:
  __SOCKET_REGISTER8(SnMR,        0x0000)        // Mode
  __SOCKET_REGISTER8(SnCR,        0x0001)        // Command
  __SOCKET_REGISTER8(SnIR,        0x0002)        // Interrupt
  __SOCKET_REGISTER8(SnSR,        0x0003)        // Status

What you end up with is a set of functions like this:
writeSnMR()
readSnMR()
writeSnCR()
readSnCR()
writeSnIR()
readSnIR()
writeSnSR()
readSnSR()

Edit: I removed the extra defines to make the example a little clearer.

Thanks Tim,
The edit certainly helped but I am still a little confused as to what is being achieved.
How would you actually call the functions from the code body.
I guess it would help if I knew what socket register are.
I may have bitten off more than I can chew.

Never give up. Never surrender. Here is the first set broken down into its components.

__SOCKET_REGISTER8(name, address) takes the parameters in the public declarations following the define and substitutes them into the two functions.

When the compiler finds this:

__SOCKET_REGISTER8(SnMR,        0x0000)        // Mode

It ends up like this:

static inline void writeSnMR(SOCKET _s, uint8_t _data)
{
   writeSn(_s, 0x0000, _data);
} 

static inline uint8_t readSnMR(SOCKET _s)
{
  return readSn(_s, 0x0000); 
}

Note it replaced the "write##name" and "read##name" with the first parameter, and the address in writeSn() with the second parameter (0000 on the first set).

I think it is starting to get clearer, I can see a light at the end of the tunnel, I just hope it is not an on coming train.
Basically what I need then is the Concatenation function ## and create something like:

#define TMR_CLR(TIMER) /
TCCR TIMER ## A = 0; /
TCCR TIMER ## B = 0; /
TCCR TIMER ## C = 0;

so that:

TMR_CLR(4)

would expand to:

TCCR4A = 0;
TCCR4B = 0;
TCCR4C = 0;

Am I on the right track?

Good try! You are on the right track.
I think it would look more like this all connected together:

#define TMR_CLR(TIMER) /
TCCR##TIMER##A = 0; /
TCCR##TIMER##B = 0; /
TCCR##TIMER##C = 0;

I have not yet tried that type of insert. It may be this

#define TMR_CLR(TIMER) /
TCCR##TIMERA = 0; /
TCCR##TIMERB = 0; /
TCCR##TIMERC = 0;

Edit: See new code below.

Making those slashes lean the right way could help, too.

This should work. I tried a version of this and it works fine.

#define TMR_CLR(TIMER) \
TCCR##TIMER##A = 0; \
TCCR##TIMER##B = 0; \
TCCR##TIMER##C = 0; 

TMR_CLR(4);

Those are backslashes at the end of each line except the last. That is all one line of code to the compiler.

ADD: It compiles but not functioning correctly with the A B or C appended. I'll try another way to append the extra letter.

Whichever combination I try i get the error:

macrotest:8: error: stray '##' in program
...

Do I need to place the #define in a separate include file, rather than at the start of the sketch?

edit: thanks Tim, I posted this before reading your update.

It is apparently that register that is causing the challenge. I can't even do this:

TCCR4A = 0;

It throws an error about ") expected before volatile". Where and how is this variable declared?

Edit: I found the problem. TCCR4A is a define, not a variable. You can't set that value.
This is in the file iom16u4.h

#define TCCR4A _SFR_MEM8(0xC0)

This compiles OK, but I have not weird up the board to test the sketch yet:

#include <avr/io.h> 
#include <avr/interrupt.h> 
// PWM - PWC - Period analiser / counter
//
//	Measure: Mark and Space 16bits each with 0.5uS reselution on 16MHz clock

#define ICP4 49

#define TMR_SETUP(TIMER) \
        TCCR##TIMER##A = 0; \
        TCCR##TIMER##B = 0; \
        TCCR##TIMER##C = 0; \
	TCCR##TIMER##B |= (1<<ICNC##TIMER); \
	bitSet(TIMSK##TIMER, ICIE##TIMER); \
	TCCR##TIMER##B |= (0<<CS##TIMER##0); \
	TCCR##TIMER##B |= (1<<CS##TIMER##1); \
	TCCR##TIMER##B |= (0<<CS##TIMER##2); \
	pinMode(ICP##TIMER, INPUT); 

unsigned int    Period    = 0;  // Rising Edge to Rising Edge value
unsigned int    StartEdge = 0;  // First Rising Edge Timer value
unsigned int    DCEdge    = 0;  // Falling Edge Timer value
unsigned int    DutyCycle = 0;  // Rising Edge to Falling Edge value


ISR(TIMER4_CAPT_vect)
{
	TCCR4B ^= (1<<ICES4); // togge ICES (Input Counter Edge Select)
	if(bitRead(TCCR4B, ICES4))
	{
		StartEdge = ICR4;
		Period    = StartEdge - DCEdge + DutyCycle;
	}
	else
	{
		DCEdge    = ICR4;
		DutyCycle = DCEdge - StartEdge;
	}
	bitSet(TIFR4, ICF4); //clear flag
}

void setup() 
{
	Serial.begin(115200);
	TMR_SETUP(4)
}

void loop() 
{
	delay(1000);
	analogWrite(4, 256 * (analogRead(0) / 1023.0));
	analogWrite(5, 256 * (analogRead(1) / 1023.0));

	Serial.print("F1");
	Serial.println(2000000.0 / Period);
	Serial.print("DC");
	Serial.println(100 - (DutyCycle * 100.0 / Period));
	Serial.print("TM");
	Serial.println((Period - DutyCycle) / 2);
	Serial.print("TS");
	Serial.println(DutyCycle / 2);
	Serial.print("TP");
	Serial.println((Period) / 2);
}

Thanks a lot Tim, I have learnt a lot today and feel more confidant working with Macros now.

The above looks like a Macro is redundant but is just a substitution into code that I already had working, to act as a test.
Ultimately I want to have all the timer functions extracted to a library so that the user can decide which timers to use depending on the target AVR chip and board, so ultimately it is not Arduino specific.

in answer to your question they are declared in avr/io.h

Cheers
Chris

Glad I could help a little. Here is the reason you cannot set that variable.
TCCR4A is a define, not a variable. It won't let you set that value. Here is the define:

#define TCCR4A _SFR_MEM8(0xC0)

This is the original working function:

void Tmr_Setup()
{
        TCCR4A = 0;
	TCCR4B = 0;
	TCCR4C = 0;

	TCCR4B |= (1<<ICNC4); // set Input Capture Noise Canceler	
	
	bitSet(TIMSK4, ICIE4); // set interupt on capture
	
	TCCR4B |= (0<<CS40); // begain timer with Prescale 8
	TCCR4B |= (1<<CS41);
	TCCR4B |= (0<<CS42);
	
	pinMode(49, INPUT); // Timer Pin as Input
}

To be honest I have no idea where to find avr/io.h I just blindly used it without ever actually reading it :slight_smile:
I normally work in PIC Assembler and am used to just including the register defines, maybe I just got lucky in this case.

A client just called so I have to drop this for now, but I will wire up my breadboard this evening, test the code, and report back if it works.
Thanks again,
Cheers
Chris

This will not work on my compiler. I use the Linux v0022 IDE.

TCCR4A = 0;

When the compiler sees that, it replaces "TCCR4A" with "_SFR_MEM8(0xC0)".

_SFR_MEM8(0xC0) = 0;

That statement is not a valid value assignment.

Interesting

I am running on WinXP Pro with Arduino022 I created a simple test as follows:

#include <avr/io.h> 

void setup() 
{
	Serial.begin(115200);
}

void loop() 
{
        TCCR4A = 0;
        Serial.println(TCCR4A, HEX);
	delay(1000);
        TCCR4A = 1;
        Serial.println(TCCR4A, HEX);
	delay(1000);
}

and my output in Serial Monitor is:

0
1
0
1
0
1
0
...

as expected.

where did you find it defined as _SFR_MEM8(0xC0) ?

Cheers
Chris

EDIT:

Ok I tracked it down

io.h --> imon2560.h --> iomxx0_1.h --> #define TCCR4A _SFR_MEM8(0xA0)

I am working on an Arduino Mega 2560, that may be where the difference is and the reason why I began the exercise.

OK! I did not have the

#include <avr/io.h>

Without it, that code won't work.

You should be good to go with the macro then!

Thanks Tim,
I modified the post above to show how the address is resolved.
Now to work out the rest of the functions I need and build my Library :slight_smile:

Cheers
Chris