When you are looking for some code for a function generator you often find examples starting like this:
int sintab2[512] = { 2048, 2073, 2098, 2123, 2148, 2174, 2199, 2224, …
Before availability of the internet, those programs were even printed in magazines. How many typos could happen, when you enter programs like this?
You could calculate the values you need in the setup part of your program, but it will need time to do so and precious space in the RAM to store the values and may be the array does not fit into the RAM of the microcontroller at all.
You could use some spreadsheet software to produce the values you need and store them in a file that can be accessed by the compiler.
Actually, compilers nowadays are able to do the job for you at compile time, or to be more precise, before compile time, that is the time when the preprocessor does its job.
It must be admitted that this solution is not very robust. It relies on features which might change at the next version of your compiler.
So, how does it work? Well, you want to fill a big array with some values. But the preprocessor does not offer any way of repeating something like this
32767*(sin( argument *RAD)+1),
and increasing the argument at each call. A strange thing, which does the trick, is this:
- Replace argument by the __COUNTER__ macro which will increment automatically each time the preprocessor finds it.
- Put this line in an include file which includes itself again and again. Make sure that the maximum of recursions is not exceeded.
Problem 1: in case the formula you are using contains the variable more than once like e.g. exp(x)*sin(x) the value of __COUNTER__ will have been increased at the second call. So __COUNTER__ * __COUNTER__ will never give you a square.
Problem 2: How do you find that maximum, which may change from compiler to compiler?
Well, you have to test and check. Once you exceeded the limit you will get an error message like this:
#include nested too deeply
So you went too far and have to reduce the depth.
At first let’s have a look at the short include file.
32767*(sin(__COUNTER__*RAD)+1),
#if __COUNTER__ < N
#include __FILE__
#endif
The first line shows the formula to get the table of values. Instead of the argument it contains the __COUNTER__ macro which is getting incremented each time it is passed.
The third line performs an include of itself as the __FILE__ macro stand for the current file. But this line will only be executed as long as the __COUNTER__ macro returns a value below a given N.
The only remaining job for the main file is to set N to a proper value and invoke that include file as often as necessary. You could add a line #undef N before redefining it to avoid warning messages of the pre-processor.
Now, how does the main file make use of this include file?
#define M 256
#define REPEATS 4
#define TOTAL (REPEATS*M/2)
// divide by two because __COUNTER__ is called twice
#define RAD PI/TOTAL
// add 1 because there is a comma after the
// last value that gives an unwanted zero.
const word PROGMEM sintab2[TOTAL + 1] = {
#define N M
#include "tab.h"
#define N M*2
#include "tab.h"
#define N M*3
#include "tab.h"
#define N M*4
#include "tab.h"
};
As you can see, calling the tab.h file once is not giving enough values so it has be called four times. The limit for __COUNTER__ has to be set to M, M*2, M*3 and M*4. So, when the program is executed it has access to 512 values of a sine table.
If you want more values of the sine table you have to increase the REPEATS definition and add more include statements.
Comments