I have been using a lot of const declarations and want to convert most of them using the #define directive, and i am in the process of learning how to use #define. I think, that I am allowed to use expressions in the value to be defined, but I am somewhat not sure how to do it.
This is an example code:
#define Timer1PrescalerSet 1 //(can be 1, 8 or 64)
#define Timer1CycleFrq 10000
#define Timer1Top (long) F_CPU/2/Timer1PrescalerSet/Timer1CycleFrq //Timer 1 is used in center-aligned mode
const are not variables, not stored in RAM. Their symbols are replaced with the values in the code and like all symbols are checked by the compiler.
the pre-processor replaces #define names with the computed value in the source which is then compiled. The compiler never sees the #define "names" used in the computation
I think I got confused by some information I read about this topic. I just now made a test from uploading some code to an Arduino nano, and it seems that most of my use of const directive do not take up RAM-space.
Some test code, out of interest. Compile results in the code.
// comment this out to use #defines
//#define USECONST
#ifdef USECONST
const uint32_t BAUD = 115200;
const unsigned Timer1PrescalerSet = 1;
const unsigned long Timer1CycleFrq = 1000L;
const unsigned long Timer1Top = F_CPU / 2 / Timer1PrescalerSet / Timer1CycleFrq;
/*
Sketch uses 1632 bytes (5%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
*/
#else
#define BAUD 115200
#define Timer1PrescalerSet 1
#define Timer1CycleFrq 10000
#define Timer1Top (long)F_CPU / 2 / Timer1PrescalerSet / Timer1CycleFrq
/*
Sketch uses 1522 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
*/
#endif
void setup()
{
Serial.begin(BAUD);
Serial.println(Timer1Top);
}
void loop()
{
// put your main code here, to run repeatedly:
}
I do not know why there is a difference in usage of program memory between the two approaches.
Without wanting to derail the topic, can somebody explain?
I guess the key takeaway is that #define macros are replaced at compile time and consume no memory themselves, while const variables might use some amount of program memory if the compiler chooses to store them rather than fully optimizing them away.
If you compile for ESP32 - do you see a different pattern for memory usage?
Yes; compiled for ESP32 Dev board.
The version that uses #define uses more program storage compared to the constversion.
This is the other way around compared to the AVR version.
#ifdef USECONST
const uint32_t BAUD = 115200;
const unsigned Timer1PrescalerSet = 1;
const unsigned long Timer1CycleFrq = 1000L;
const unsigned long Timer1Top = F_CPU / 2 / Timer1PrescalerSet / Timer1CycleFrq;
/*
Sketch uses 1632 bytes (5%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
ESP32
Sketch uses 278583 bytes (21%) of program storage space. Maximum is 1310720 bytes.
Global variables use 20800 bytes (6%) of dynamic memory, leaving 306880 bytes for local variables. Maximum is 327680 bytes.
*/
#else
#define BAUD 115200
#define Timer1PrescalerSet 1
#define Timer1CycleFrq 10000
#define Timer1Top (long)F_CPU / 2 / Timer1PrescalerSet / Timer1CycleFrq
/*
Sketch uses 1522 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.
ESP32
Sketch uses 278611 bytes (21%) of program storage space. Maximum is 1310720 bytes.
Global variables use 20800 bytes (6%) of dynamic memory, leaving 306880 bytes for local variables. Maximum is 327680 bytes.
*/
#endif
const is a type qualifier keyword. It is used to instruct the compiler to mark the stored value as "read-only". For example:
1. byte y1 = 0x35;
y1 is stored in RAM and can be modified during run-time.
2. const byte y2 =0x65;
y2 is stored in RAM but marked read-only using `const` keyword; so, it can't be changed during run-time
3. const byte y3 PROGMEM = 0x75;
y3 is stored in program memory but marked read-only using `const` keyword; so, it can't be changed during run-time by executing the following code:
byte y3 PROGMEM = 0x89;
A constant variable is a variable whose value is initialized to a constant and then marked read-only using the const keyword.
5. A directive is a command to the compiler (not to the CPU). For example:
Tthe word "directive" is not a normative term in C++. The C++ standard refers to these as "preprocessor directives" for lines starting with # such as #include, #define, or #if.
âžś The term "directive" appears in the standard text to describe these constructs, but it is not a language concept with its own formal semantics like "expression, "statement", "type," or "class."
It's just the English word used to describe preprocessor lines.
So I think it might have to do with the 32 bit versus 8 bit platform as you are using 32 bit constants and extra code needed on the AVR to bring the bytes back from flash when executing the setup whereas on the ESP32 it can directly read the const in flash.
#define Timer1PrescalerSet 1 //(can be 1, 8 or 64)
#define Timer1CycleFrq 10000
#define Timer1Top (F_CPU/2/Timer1PrescalerSet/Timer1CycleFrq) //Timer 1 is used in center-aligned
ensuring that the actual calculation is done by the compiler (which otherwise may or may not happen depending on the core or other reasons.) The type (long) in this case is redundant, since F_CPU is defined as an UL, and incorrect, since it should be an unsigned type.
From my experiments and your posted documents, I now know that there is no functional difference between the following two styles. However, the second style is a somewhat “illiterate” way of programming. The lexical analyzer in the compilation process will remove the whitespace and will take #define as a single token.
You are going against the grain. Arduino says do not use #defines. const is the proper language construct. There are several good reasons for this, the first being that #define is a text tool, and const is a part of the language.