Official Arduino documentation refers to the Arduino programming language. It is just in fact C++.
AVR Libc contains a ton of useful functions.
SRAM is a scare resource when working with most ATmegas (the Atmega328P only has 2K bytes worth) and statically initialized variables and string constants quickly eat it up. It turns out you can store static data in "program space" (aka flash memory) which is usually available in abundance. The catch is you have to copy data out to SRAM before you work with it.
For example here's a statically allocated array of values:
static const int8_t PROGMEM ledstate[] = { 1, 2, 1, 36 };
Use pgm_read_byte() to read a byte from program space:
int16_t val; val = ((int8_t)pgm_read_byte(ledstate[2]);
The PSTR() macro stores a string in program space. The F() macro uses PSTR() to store a string in program space and also flags this to library routines such as Serial.print().
printf_P() uses a F() format string from program space. In addition all members of the printf() family interpret %S arguments as program space strings.
#define STR(s) _STR(s) #define _STR(s) #s
Here's an example of turning a number into a string:
#define PIN_LED 3 Serial.print(F("LED is pin " STR(PIN_LED) "\n"));
which prints "LED is pin 3\n".
Here's a macro that is used to subtract micros() values (which are unsigned long) and handles overflow correctly:
/* Subtract t2 from t1; handles overflow correctly */ #define MICROS_SUB(t1, t2) \ (((t1) >= (t2)) ? (t1) - (t2) : (t1) + (UINT32_MAX - (t2)))
It's common to print something on the serial port from setup():
void setup() { Serial.println(F("howdy")); }
However when serial port is USB the message is never seen because the USB serial port disconnects on reset. At least with parts such as the ATmega32U4 "Serial" does not exist until it is connected. This makes it possible to delay the message until then by moving it into loop():
void loop() { static boolean init = 0; if (!init && Serial) { Serial.println(F("howdy")); init = 1; } }
Although larger parts such as the ATmega1284 have 128K flash your code must fit in the first 64K of address space due to 16 bit pointers. However it's possible to store data in the upper 64K in a manner similar to how PROGMEM works. However anything in the upper 64K has "far" addresses which are 24 bits wide.
First you need to define a new section, here's a compiler flag that passes a linker flag:
-Wl,--section-start=.progmem64k=0x10000
Next, define a macro that can be used to place data into the new section:
#include <avr/pgmspace.h> #define PROGMEM64K __attribute__((section(".progmem64k")))
Since the cross compiler is configured to know that pointers are 16 bits wide you need to use the pgm_get_far_address() macro to get the "far" address of data in the upper 64K. This returns a uint32_t (32 bits) although only the lower 24 bits are used.
Here's some example code:
const char msg_howdy[] PROGMEM64K = "howdy\r\n"; void setup() { uint32_t x; char ch; x = pgm_get_far_address(msg_howdy); Serial.print(F("msg_howdy far addr is ")); Serial.println(x, HEX); Serial.print(F("msg_howdy: ")); while ((ch = pgm_read_byte_far(x++)) != '\0') Serial.write(ch); }
When this sketch is run it outputs:
msg_howdy far addr is 0x10000 msg_howdy: howdy
Note that PROGMEM data is stored in the beginning of the .text segment and have low addresses. This means text strings stored in PROGMEM are pretty much guaranteed to reside in the first 64K of address space. If you are running out of .text space for instructions, moving them to the upper 64K will always help.
Copyright © 2011, 2012, 2013, 2015, 2017, 2018, 2019, 2020, 2021, 2022
Craig Leres