2

This is the first time I'm using macros in C and I'm trying to replace a large section of code that I'd normally place into a function with a macro. This is a part of an interrupt which will be used quite often and therefore I need to optimize it as much as I can. After reading the documentation, I've seen that the compiler doesn't support function inlining and I want to avoid the function call overhead.

The code itself sends data to a serial-in parallel-out shift register and as far as I can see, there's no shorter way to write the piece of code I need.

I'm using C18 compiler version 3.41 and MPLAB X IDE.

So here's the code I'm using in function form:

void first_one(void)
{
   //3 invisible zeroes
            LATBbits.LATB1=0; //data set to zero

            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//third clock
            LATBbits.LATB0=0;
            //end of invisible zeroes

            //two visible zeroes    
            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;
            //end of two visible zeroes

            LATBbits.LATB1=1;//Data is now one

            LATBbits.LATB0=1;
            LATBbits.LATB0=0;
            //one 

            LATBbits.LATB1=0;//Data is now zero

            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;

            //after this, everything should be in place
            LATBbits.LATB0=1;
            LATBbits.LATB0=0;
}

I've turned the function into this macro:

#define first_one() {  \
\
            LATBbits.LATB1=0;\               
                              \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
                                \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
                            \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
            \                                
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
            \
            LATBbits.LATB1=1;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\    
\
            LATBbits.LATB1=0;\
             ^^^ The syntax error is here!
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
                     }

So what am I doing wrong?

Update: I removed the comments and am now getting a syntax error in a different location.

AndrejaKo
  • 1,721
  • 5
  • 25
  • 41
  • Why do you even think you need to turn the function into a macro ? Just make it an inline function if function call overhead is significant. – Paul R May 05 '12 at 17:00
  • @Paul R As I said in the question, the compiler does not support function inlining. – AndrejaKo May 05 '12 at 17:02
  • 1
    ... but as Paul said, do you really thing the function call overhead is significant here - this is not a one line function after all. Looks like a [premature optimisation](http://c2.com/cgi/wiki?PrematureOptimization) to me. – Clifford May 05 '12 at 20:30
  • @Clifford Yes, I'm well aware of "the root of all evil". Yes, the function call overhead is a problem. At current operating frequency of the microcontroller I have barely enough room to service the interrupt quickly enough and there is very little time for other things the microcontroller should be doing. Yes, I've considered increasing the frequency and yes, that's a big problem so it's better not to increase it. No, I can't move to a more efficient architecture. With that said, I believe that my efforts to optimize this are justified. – AndrejaKo May 05 '12 at 20:51
  • @AndrejaKo: If you are failing to meet real-time dead-lines, the need for this kind of micro-optimisation suggests that you had either run out of ideas or capacity. If the latter, then your attempt is perhaps heroic, but may ultimately be doomed. If the former, then there is hope, but recognising that as the issue is difficult for obvious reasons; if a solution is possible it will be at the design level rather than the coding level. For example all but the 18 pin PIC18 parts have an SPI port that can perform the serial out in hardware, reducing the code space *and* CPU overhead. – Clifford May 07 '12 at 07:42
  • There are embedded compilers that don't support inlining? Get a new compiler. Surely there must be a well-maintained GCC port for PIC. – Lundin May 07 '12 at 19:36
  • @Lundin PIC18 and below architecture is incompatible with GCC. The XC8 compiler, which is supposed to have support for inlining, just came out a month ago and still has no support for peripheral libraries and is full of bugs. Also the code isn't very portable from one compiler to another. – AndrejaKo May 07 '12 at 20:24

4 Answers4

8

Check there is no spaces after the \ tokens, some compiler issue a compile error for this.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • I checked for spaces, confirmed that there aren't any and the error is still there. – AndrejaKo May 05 '12 at 17:19
  • Actually it turns out that this was the problem. After restarting the IDE few times and seeing that the program compiled fine, I tried adding a space after `\` and got the syntax error just like I initially did. – AndrejaKo May 05 '12 at 17:43
  • 2
    Here's a tip: In your editor select/highlight all the text in the macro; any trailing space will show up as a coloured block at the end of the offending line. You can even do that with the text in posted question and see where the problem is. – Clifford May 07 '12 at 07:44
3

Lines are spliced before comments are removed, so the \ in \//3 invisible zeroes does does not continue the line.

You need either to remove the comment or use a C-style comment (/* 3 invisible zeroes */) and place the comment before the \ that continues the line.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Looks like the main problem aren't the comments. I removed them and am still getting a syntax error. – AndrejaKo May 05 '12 at 16:45
  • What is the syntax error? Look at the preprocessed source and find out what's wrong (compile with `-E` on gcc and Visual C++). – James McNellis May 05 '12 at 16:48
  • The syntax error is `Error: syntax error` so it isn't very helpful. Visual Studio and GCC do not support the platform I'm writing code for right now. – AndrejaKo May 05 '12 at 16:54
  • Whatever compiler you are using, it should support standalone preprocessing. Consult its documentation. – James McNellis May 05 '12 at 17:54
1

The problem is with the comments and the way the preprocessor deals with them. Remove the comments and this should work fine. Alternativly use /* Comment */

John Wheal
  • 9,908
  • 6
  • 29
  • 39
1

Three suggestions:

First, make sure that there aren't any trailing spaces after each \.

Second, drop the () from the macro name if it's not meant to take any arguments. edit struck per comments below.

Finally, wrap the contents of the macro in a do {...} while(0) (with no trailing semicolon). This way, when you write first_one(); in your code, you won't have a spurious semicolon after the closing brace.

In short,

#define do_first         \
  do {                   \
    LATBbits.LATB0 = 1;  \
    ...                  \
  } while(0)

edit Lundin points out that this is old-fashioned and unnecessary. I always believed it was required to avoid a diagnostic if a macro expanded to a statement of the form {...}; -- apparently I was wrong. I still prefer it as a stylistic choice, though.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 4
    I never omit the () from macros that are supposed to act like functions, even if they take no arguments. It makes the code clearer to read (this is a function like statement) rather than just a bare symbol. – Adam Casey May 05 '12 at 19:21
  • I agree, always use () for macros to make them look like C, and to make them compatible with inline functions, should you get a standard C compiler in the future. Also, the do-while advice is old-fashioned. All modern C coding standards enforces {} after each statement, to reduce the number of bugs drastically. So the do-while is unnecessary if you have some coding discipline. – Lundin May 07 '12 at 19:34
  • @Lundin: I'll be damned. I could have *sworn* `{...};` would have caused heartburn, but gcc swallows it just fine, even with -pendatic. And I'll accept the reasoning for keeping the `()`. I tend to avoid using macros for anything beyond symbolic constants -- just too much heartburn. – John Bode May 07 '12 at 20:27
  • @JohnBode You are not alone, even the C authorities in the MISRA-C committee shared this belief, [see this](http://www.misra.org.uk/forum/viewtopic.php?f=72&t=1096). However, that rule is removed in the draft for MISRA-C:2012. – Lundin May 08 '12 at 14:53