[solved]Understanding macros w/CV

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

Hello,

I wanted to write a macro to pulse an io pin in Codevision utilizing the provided bit level access CV provides.

Why is it something like this works as expected

#define WHATEVER  PORTC.x 
WHATEVER =  1;
WHATEVER = 0;

yet this does not

#define WHATEVER  PORTC.x
#define up(this) this = 1
#define down(now) now = 0
#define pulse(pin) do {up(pin); down(pin);} while(0)
 pulse(WHATEVER);

even this does not work as expected

 up(WHATEVER);

I'm aware of another more portable way to write a macro that would more or less perform the same task. However I was wondering if someone could explain why this does not work so I may better understand the subject.

Thank you for your time

Self proclaimed Captain Link

Last Edited: Mon. Feb 14, 2011 - 11:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well what do you expect will happen when you write for example:

pulse(pin);

Since you have "while(0)" in the macro, that code will probably be removed by the optimized. So there will be no pulsing. Is that not the result?

Tell us what you mean by "it does not work".

/Jakob Selbing

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

It is hard to say without more information. I'm guessing that your problem may have to do with the number of times the input text is scanned after a macro substitution. The actual number of times is supposed to be until there are no possible remaining substitutions. Do you have or can you examine any of the CV intermediate files?

We never have time to do it right,
but we always have time to do it over

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

jaksel wrote:
Well what do you expect will happen when you write for example:

pulse(pin);

Since you have "while(0)" in the macro, that code will probably be removed by the optimized. So there will be no pulsing. Is that not the result?

Tell us what you mean by "it does not work".


I agree that "it doesn not work" is vague.

The do {...}while(0) is a way of "executing" the loop exactly one time. The language guarantees this regardless of the outcome of the test, that is why the construct is there. It is semantically OK to optimize away the test at the end but not the body of the loop. The loop construct is however redundant since placing the statements inline will accomplish the same thing.

We never have time to do it right,
but we always have time to do it over

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

@jaksel

I read once using a "do {..... } while(0);" statement was a good std practice in a macro of this type to ensure all code within said macro is executed. So I expected that :).
However I'll have to check and see if it's getting optimized out. Thanks for the heads up. Even if it is being optimized out I previously removed that portion of the code to the same result prior to posting. If it was a problem it's not the only one.

Self proclaimed Captain Link

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

I've gotten away from the dot syntax quite a bit over the past few years. The main reasons are the "new" Mega I/O map has pushed so many I/O registers above the SBI/CBI limit, and apps with PORTG and above (Mega64, Mega640, ...) aren't reachable either. So instead of mix-and-matching, I just try to be consistent.

I suspect your situation has to do with token-pasting and other preprocessor facilities that are too sophisticated for an old bit-pusher to handle.

(Somewhere in the temp files is the preprocessor output; take a peek at that.)

Generally, I expand the definitions a bit, and the below works fine:

#define	OUTPUT_EXTEND		PORTB.4
#define	OUTPUT_RETRACT		PORTB.2
#define	OUTPUT_LAMP1		PORTB.1
#define	OUTPUT_LAMP2		PORTB.0

#define	OUTPUT_ON	1	// active high
#define	OUTPUT_OFF	0

...
			OUTPUT_EXTEND = OUTPUT_ON;	// "forward"
			OUTPUT_RETRACT = OUTPUT_OFF;

I do the toggle functions the same straight-forward way, and not parameterized:

#ifdef DEBUGGING
#define TOGGLE_SPARE() PINA.3 = 1
#define SET_SPARE() PORTA.3 = 1
#define RESET_SPARE() PORTA.3 = 0
#else
#define TOGGLE_SPARE()
#define	SET_SPARE()
#define RESET_SPARE()
#endif

Quote:

even this does not work as expected
Code:
up(WHATEVER);

Hmmmm--it does for me, with 2.04.5:

#include 

#define WHATEVER  PORTB.1
#define up(this) this = 1
#define down(now) now = 0
#define pulse(pin) do {up(pin); down(pin);} while(0)

 
void main(void)
{
   up(WHATEVER);
   down(WHATEVER);
   pulse(WHATEVER);
}
                 ;void main(void)
                 ; 0000 000A {
                 
                 	.CSEG
                 _main:
                 ; 0000 000B    up(WHATEVER);
000064 9a29      	SBI  0x5,1
                 ; 0000 000C    down(WHATEVER);
000065 9829      	CBI  0x5,1
                 ; 0000 000D    pulse(WHATEVER);
000066 9a29      	SBI  0x5,1
000067 9829      	CBI  0x5,1
                 ; 0000 000E }
                 _0xE:
000068 cfff      	RJMP _0xE

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Does CV have something like -E so you can see the source after pre-processing?

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

Yep - confirmed as compiling perfectly (by analysis of the ASM generated) with CV 2.05.0

...and I've learnt something about #defines - I didn't think you could do that sort of thing (must go and read K&R about it). Thanks :D

EDIT: ...and leaving out the do{}while(0) around the #define pulse(pin)... gives the same ASM output

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

Quote:

EDIT: ...and leaving out the do{}while(0) around the #define pulse(pin)... gives the same ASM output


That is a known construct to make a single-statement macro.

I'm guessing Personman is using CV 1.x. 2.x uses a new "ANSI front end" and may more rigorously handle all C constructs.

Quote:

Does CV have something like -E so you can see the source after pre-processing?

Quote:
The following Preprocessor options can be set:
· Create Preprocessor Output Files - when enabled, an additional file with the .i extension will be created for each compiled source file. The preprocessor output files will contain the source files text will all the preprocessor macros expanded. Enabling this option will slow down the compilation process.
...


The .i file from my test program above:

#pragma used+
sfrb PINA=0;
sfrb DDRA=1;
sfrb PORTA=2;
sfrb PINB=3;
sfrb DDRB=4;
sfrb PORTB=5;
sfrb PINC=6;
sfrb DDRC=7;
sfrb PORTC=8;
sfrb PIND=9;
sfrb DDRD=0xa;
sfrb PORTD=0xb;
sfrb TIFR0=0x15;
sfrb TIFR1=0x16;
sfrb TIFR2=0x17;
sfrb PCIFR=0x1b;
sfrb EIFR=0x1c;
sfrb EIMSK=0x1d;
sfrb GPIOR0=0x1e;
sfrb EECR=0x1f;
sfrb EEDR=0x20;
sfrb EEARL=0x21;
sfrb EEARH=0x22;
sfrw EEAR=0X21;   
sfrb GTCCR=0x23;
sfrb TCCR0A=0x24;
sfrb TCCR0B=0x25;
sfrb TCNT0=0x26;
sfrb OCR0A=0x27;
sfrb OCR0B=0x28;
sfrb GPIOR1=0x2a;
sfrb GPIOR2=0x2b;
sfrb SPCR=0x2c;
sfrb SPSR=0x2d;
sfrb SPDR=0x2e;
sfrb ACSR=0x30;
sfrb OCDR=0x31;
sfrb SMCR=0x33;
sfrb MCUSR=0x34;
sfrb MCUCR=0x35;
sfrb SPMCSR=0x37;
sfrb SPL=0x3d;
sfrb SPH=0x3e;
sfrb SREG=0x3f;
#pragma used-

#asm
	#ifndef __SLEEP_DEFINED__
	#define __SLEEP_DEFINED__
	.EQU __se_bit=0x01
	.EQU __sm_mask=0x0E
	.EQU __sm_powerdown=0x04
	.EQU __sm_powersave=0x06
	.EQU __sm_standby=0x0C
	.EQU __sm_ext_standby=0x0E
	.EQU __sm_adc_noise_red=0x02
	.SET power_ctrl_reg=smcr
	#endif
#endasm

void main(void)
{
PORTB.1 = 1;
PORTB.1 = 0;
do {PORTB.1 = 1; PORTB.1 = 0;} while(0);
}

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Personman wrote:
@jaksel

I read once using a "do {..... } while(0);" statement was a good std practice in a macro of this type to ensure all code within said macro is executed. So I expected that :).
However I'll have to check and see if it's getting optimized out. Thanks for the heads up. Even if it is being optimized out I previously removed that portion of the code to the same result prior to posting. If it was a problem it's not the only one.


Sorry, I didn't think through the syntax. Of course the body of the loop should execute once, and then exit :oops:

But again, what is the outcome when you run it? Do you have something hooked up to the pin so that you can see if anything happens?

Note that if you want a single pulse, you must make sure the initial state of the pin is low. Maybe this syntax would be safer (ie it does not rely on an initial low state):

#define pulse(pin) do {down(pin); up(pin); down(pin);} while(0)

/Jakob Selbing

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

Quote:

But again, what is the outcome when you run it?

Hmmm--I guess I assumed it was a compile-time error.

So, Personman, define

Quote:

does not work as expected

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

CV version = 2.04.6 Evaluation

At this time I have a LED & resistor hooked to the pin and I paced a pair of delays in the code so I would see the blink and verify the pulse was happening. I was getting at best a solid lit LED leading me to believe my AVR was reseting. After further investigation that was not the issue.

:oops: After reading this was working for theusch I retyped the code again and now all is working as expected, the led is blinking. This was part of a diagnostic of a larger project. I must have missed a simple typo that the compiler would not have been worried about. As many times as I re-read & modified the code I should have caught whatever but I guess I didn't.

My apologies and thanks,
Todd

Self proclaimed Captain Link