25

I have a PIC18F46K22 and program it with the XC8 compiler. In the end, I'll have a system like a pc with stdin and stdout. So in the main loop there will be a function which is checking if there is new input. If there's input, a function will be called accordingly. So for example when I input an A on stdin, the PIC will run a function like function_A instead of function_B which is called when I input a B.

When the PIC is done with the function, I want that the new input is sent to the function. So when pressing A opens the RS232 transmitter, from that moment every input will be sent over RS232. In the end the project is a stand-alone text editor. So when pressing A opens the file system, from that moment you aren't text editing anymore but looking through a list of files. That means that pressing Up and Down means something different than in the text-editing environment.

I've done a lot thinking on how to program this in C. I thought this up last night and would like to know if it's possible and if so, how. What I want to do is:

  • The main function calls a function like function_A
  • function_A changes a global variable function_addr to the address pointer of function in_function_A
  • From that moment, main calls the function at function_addr when there is new input.

So what I'd need is a main function which checks if function_addr is zero. If it is, a 'normal' function should be called, like function_A. If it isn't, the function at function_addr should be called. I also need a function_A which changes the function_addr to a pointer to in_function_A.

Note: when the filesystem function should be closed, is_function_A should just change function_addr to 0.

So basically my question is how can I

  • Get the address of a function (and store it in a variable)
  • Call a function at a specified address
  • 6
    Off topic. Belongs on stackoverflow.com. – user207421 Jan 25 '13 at 11:15
  • To me, a state machine approach is much less risky than dealing with function pointers. Your input byte is passed to a state machine structure (could be as simple as a switch-case) which jumps to various bits of code as a function of the state variable or variables. Also, you're not totally clear on where your stdin is coming from (not that it really matters much, but I'm curious). – Adam Lawrence Jan 25 '13 at 14:27
  • 10
    @EJP I disagree. Just because there is an overlap does not mean it has to be on one site or the other. It is asking questions related to the design and programming of low level embedded systems, that seems on topic either place. – Kortuk Jan 25 '13 at 15:16
  • State machines can be based on function indirection: the call to the state transition function returns a new state transition function. – Kaz Jan 25 '13 at 22:26
  • @Kortuk Well *I* disagree. It is a computer programming question, and has precisely nothing to do with EE except insofar as you want to represent that EE includes computer programming, which from very long experience is not a belief that I share. There is also the question of where the relevant expertise is most concentrated, and re that there cannot be any doubt that SO wins by a mile. So asking here instead of there is basically not a rational mode of enquiry. – user207421 Feb 02 '13 at 09:17
  • 1
    Lets have this discussion [here](http://meta.electronics.stackexchange.com/questions/2622/are-pure-c-questions-on-topic). –  Feb 02 '13 at 09:42
  • You may find it useful to look at examples of function pointers in a book such as https://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays#Pointers_to_Functions – davidcary Oct 19 '13 at 14:09

2 Answers2

23

A function

int f(int x){ .. }

Get the address of a function (and store it in a variable)

int (*fp)(int) = f;

Call a function at a specified address

int x = (*fp)( 12 );
Wouter van Ooijen
  • 48,407
  • 1
  • 63
  • 136
  • 3
    +1, obvious in the C and ASM world, but not so obvious to those who started out with OO languages. – Anindo Ghosh Jan 25 '13 at 08:42
  • 4
    The fp call can also be written as 'int x = fp(12);' to increase readability. – Rev Jan 25 '13 at 09:16
  • 1
    I have to admit, working mainly in C# and Java currently, I miss those good old function pointers from C sometimes. They are dangerous when not done exactly right, and most tasks can be accomplished in other ways, but they *sure are useful* those few times when they really do come in handy. – user Jan 25 '13 at 10:20
  • @MichaelKjörling: So true. I am constantly switching between embedded C and C# programming (even implementing similar code for communication between device and PC) and as mighty as C# might be, i still enjoy C so much. – Rev Jan 25 '13 at 12:51
  • @Michiael: I am not a heavy Java user, but can't you simply wrap the function as a class method, and store (a pointer to) that class? – Wouter van Ooijen Jan 25 '13 at 13:44
  • @Wouter You probably could. My point isn't that you can't do something similar, it is that actual pointers can be extremely useful the few times that you can't, or when doing the same thing through other means requires a lot more code. I can't think of a good example off the top of my head, but that probably has more to do with the fact that these days I mostly do programming where the need hasn't come up. Pointers (especially in an unchecked context) are dangerous, and notoriously hard to get right, but *very occasionally*, bare pointers is *exactly* the tool you need for the task at hand. – user Jan 25 '13 at 14:51
  • @MichaelKjörling: with C# you can use variables of delegate type (especially the Func<...> and Action<...> types) in more or less the same way as C uses function pointers. This is especially true when you are setting the delgate equal to some static method. (For non-static methods they dop not act like c++'s pointer-to-member construct, but instead keep a refernce to some specific instance). Also with C# if your code will run with full trust (like desktop apps), you can utilize raw non-function pointers with "unsafe" code. Java deliberately lacks both features. – Kevin Cathcart Jan 25 '13 at 18:55
  • 2
    `fp(12)` does not increase readability over `(*fp)(12)`. They have different readability. `(*fp)(12)` reminds the reader that `fp` is a pointer, and also resembles the declaration of `fp`. In my mind, this style is associated with old sources, like X11 internals, and K&R C. One possible reason it might be associated with K&R C is because in ANSI C, it is possible to declare `fp` using function syntax, if `fp` is a parameter: `int func(int fp(double)) {}`. In K&R C that would have been `int func(fp) int (*fp)(double); { ... }`. – Kaz Jan 25 '13 at 22:30
  • It might be worth discussing the implications of how function pointers are implemented on a bare metal lower end pic. – Kortuk Feb 02 '13 at 16:43
  • @Kortuk the context of the question is a PIC18F46K22 , hardly a low-end PIC. But on 14-bit cores a function pointer is not much different from any other pointer. On a 12-bit core things are complicated, but I doubt one would use function pointers on a chip with a 2-level stack anyway. – Wouter van Ooijen Feb 02 '13 at 17:46
  • @WoutervanOoijen Many users dont realize that they might be doing something very crazy. We had some lower end devices that had function pointers because that is how they had done it before. There are much nicer things then PIC18, it just might be worth expanding a bit to include the microchip implications. Only a suggestion. – Kortuk Feb 02 '13 at 17:58
18

While Wouters answer is absolutely correct this maybe more beginner friendly and a better example regarding your question.

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

If it is not obvious that the function pointer actually has a target function address assigned, its wise to perform a NULL check.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
Rev
  • 10,017
  • 7
  • 40
  • 77
  • 8
    +1 for the null check, **but do note** that the compiler might not guarantee that the value at the address in RAM that `fp` happens to occupy is actually NULL initially. I would do `int (*fp)(int) = NULL;` as a combined declaration and initialization to be certain - you don't want to jump to some random memory location because of some goofy value that just happened to be there. Then just assign `fp` like in your `Example()` function. – user Jan 25 '13 at 10:19
  • 1
    Thanks for the comment, i added the NULL initialization. – Rev Jan 25 '13 at 12:47
  • 4
    @MichaelKjörling good point, but if `fp` is a "global variable" (as int he code above), then it is guaranteed to be initialized to `NULL` (without having to do so explicitly). – Alok Jan 25 '13 at 17:59