© Norman Dunbar 2020
N. DunbarArduino Software Internalshttps://doi.org/10.1007/978-1-4842-5790-6_2

2. Arduino Compilation

Norman Dunbar1 
(1)
Rawdon, West Yorkshire, UK
 

This chapter is all about what happens when you compile an Arduino sketch and how the various header files are used. Hopefully, by the time you have read (and understood) this chapter of the book, you’ll have a much better idea of what happens during the compilation of an Arduino sketch. However, before we dive into the gory details of a sketch’s compilation, we need to understand a bit about some of the text files that live in and around the $ARDINST directory.

These files are used to set up the IDE’s menu options and to define the AVR microcontroller and Arduino board to be used. Additionally, the IDE needs to know how to compile and upload sketches, and with lots of different boards nowadays, not just those with AVR microcontrollers, these numerous text files help the IDE configure the build tools and so on, for the specific board chosen from the Boards menu in the IDE.

Once we have discussed the various text files, we can then get down and dirty in the compilation process and also take a look at the hidden C++ files that the Arduino environment keeps well away from us.

2.1 Preferences.txt

The file preferences.txt holds all the preferences for the Arduino IDE and under recent versions of the IDE is no longer found within the location of the various IDE files, but in a separate area so that future upgrades to the IDE do not overwrite any changes that you make to the file. This explains why, when you configure the IDE on one version, an upgrade will pick up your preferences without you having to reapply them all every time you upgrade.

You should find the file in one of the following locations, however, as the file is created when you first run the IDE. If you have not yet done so, there will not be a preferences.txt file to be found. The initial set of defaults is defined in the file $ARDINST/lib/preferences.txt so those are the ones that will get written to the preferences.txt file, in the appropriate location, on first execution of the IDE. You may also delete the preferences.txt file if your edits have rendered it unusable, and it will be recreated by the IDE next time you open it.

You could edit $ARDINST/lib/preferences.txt to set your own preferred default settings, but as the file will be overwritten by IDE updates, it’s probably not a good option to consider.

The one thing about looking in $ARDINST/lib/preferences.txt is the fact that everything is commented nicely to advise you as to what the options are used for. The file which the IDE actually uses is not commented at all. If you need to know what you are about to change, look at the $ARDINST/lib/preferences.txt file but change the one that is in the correct location for your system. The file used by the IDE is found in a number of places, depending on your operating system of choice:
  • On Linux – Look in /home/<YOUR_NAME>/.arduino15.

  • On Windows 7 – Look in C:Users<YOUR_NAME>AppDataLocalArduino15. I believe that on older versions of Windows, the file can be found in c:Documents and Settings<YOUR_NAME>Application DataArduino or even C:Program Files (x86)Arduinolib which I believe is where 32-bit applications get installed on 64-bit machines.

  • On MacOS – I believe you can look in /Users/<YOUR_NAME>/Library/Arduino, but I don’t have access to a Mac to check, sorry.

The easiest way to determine the location of the preferences.txt file is to open the IDE and select File ➤ Preferences; and on the “Settings” tab, at the bottom, you will see the full path to the preferences.txt file documented. Take heed of the warning to only edit the file when the IDE is closed – the IDE writes to the file when you shut it down and will overwrite any changes you made if the IDE was open when you changed the file.

The preferences.txt file contains all the configuration changes that you made using the IDE. The changes you make here will be saved between IDE upgrades. There are some additional preference changes that you need to make by editing the preferences file directly as the IDE doesn’t “surface” those options. A couple of examples follow.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figa_HTML.gif If you have your preferences nicely set up, beware if you subsequently install and use the new Arduino command-line utility arduino-cli (see Chapter 6, Section 6.2, “Arduino Command Line,” for details). It uses the same location for all its files and will pick up whatever preferences you have configured for the IDE.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figb_HTML.gif If, by some chance, you make a mistake editing the file and things stop working (properly), you can reset everything to defaults by simply deleting the preferences.txt file while the IDE is closed and running the IDE again.

2.1.1 Using an ICSP for All Uploads

Are you using an ICSP (In-Circuit System Programmer) to do all your uploads? Do you get fed up having to configure it in every sketch you write and try to upload? Wouldn’t it be nice to tell the IDE that you are always using an ICSP? Try this:
  • Close the IDE if it is open.

  • Edit the preferences.txt file in your favorite text editor. (No, do not use Microsoft Word!)

  • Search for “upload.using”. It should currently look like this:

    upload.using=bootloader
  • Change it to use the name of the device you are using. The device name you change it to must match one of the device names in the file $ARDINST/programmers.txt. In my case, I use a USB Tiny clone, from eBay, and I set my option to the following:

    upload.using=USBtinyISP
  • Save the file.

With this done, all sketches will now default to using the ICSP rather than the bootloader. This means that I no longer have to worry about remembering to change the programmer in the IDE, and, as a bonus, I will always overwrite the bootloader area and regain the use of that part of the Flash RAM for my own use. My Uno board will have an extra 512 bytes (1.5625% of the total) of flash for my programs, while my Duemilanove will regain an extra 2 Kb or 6.35% from the bootloader space.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figc_HTML.gif Uploading with any ICSP device does still require you to press the Shift key when you click the upload button or to select Sketch ➤ Upload Using Programmer, even with this preference set.

I can still set the IDE to use a bootloader though. I just have to remember to select it from the Tools menu when I wish to create a sketch for a system that I cannot, or don’t want to, use the ICSP for uploads.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figd_HTML.gif What’s an ICSP? Normally beginners would use a USB cable between the computer and the Arduino to upload programs. However, look at your Arduino and see if you can see a set of six pins in two rows of three. My Duemilanove has them beneath the reset switch. Those pins are where an ICSP can be plugged in to program the Arduino. Using one of these frees up the space taken by the bootloader program and gives you a bit more program space.

Unfortunately, it does prevent the Arduino from talking back to your computer using the Serial Monitor facility – Tools ➤ Serial Monitor. You will need an ICSP if you have to replace the ATmega328P on your board, and it comes with the default fuse settings. You will need to purchase one with an Uno bootloader already programmed in or use an ICSP to program your own.

My ICSP is from an eBay seller “finetech007” which no longer exists. There are lots of them if you search for “usbtiny isp” – here’s one example [www.ebay.co.uk/itm/USBTiny-USBtinyISP-AVR-ISP-programmer-for-Arduino-bootloader-Meag2560-uno-r3-CF/191780957944?epid=1138358692&hash=item2ca7092ef8:g:4-UAAOSwhvpd-fY7], identical to mine. (Sorry about the length of that URL!)

2.1.2 Change the Action of Home and End Keys

../images/494109_1_En_2_Chapter/494109_1_En_2_Fige_HTML.gif I’m reliably informed that this applies to Apple Mac users. It certainly has no effect on Windows 7 or Linux.

In the editor, when you press the Home key, the caret jumps to the very start of the sketch. When you press the End key, it jumps to the very end of the sketch. Apparently, this gets quite annoying when you expect the caret to be positioned at the start or end of the line you are editing. This sort of thing definitely needs changing!

There isn’t an option in File ➤ Preferences which enables this action to be changed so that the cursor goes to the start or end of the current line and not to the start or end of the current sketch. The preferences.txt file must be edited directly:
  • Close the IDE, if it is open.

  • Edit the preferences.txt file.

  • Look for the following setting:

    editor.keys.home_and_end_beginning_end_of_doc = true
  • Change it to the following and save the file:

    editor.keys.home_and_end_beginning_end_of_doc = false

When you next open the IDE and load a sketch, the Home and End keys should now do your bidding.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figf_HTML.gif Issue 3715 on the GitHub issues page for the IDE has some interesting details about this preference. It only exists from version 1.6.6 onwards. Prior to that, it was called editor.keys.home_and_end_travel_far.

In the first incarnations of version 1.6.6, the setting was coded backward. Setting it to false meant that the Home and End keys sent the cursor to the start or end of the document. You had to set it to true to get it to go to the beginning or end of the current line.

Since August 28, 2015, that was fixed; and it now works as it should. You can find all the details at https://github.com/arduino/Arduino/issues/3715.

2.1.3 Setting Tab Stops

Now, you would think that an editor, for writing code, would at least allow you the ability to adjust the width of the tab stops and whether or not they are to be converted into spaces. Not so the Arduino IDE!

All is not lost, as we do have that ability, but it involves editing the preferences.txt file again:
  • Close the IDE if you currently have it open.

  • Edit the preferences.txt file.

  • Look for the following two lines:

    editor.tabs.expand=true
    editor.tabs.size=2
  • Change the second line as per the following:

    editor.tabs.expand=true
    editor.tabs.size=4
  • Save the file.

This causes tabs to indent four characters from the default of two characters. I don’t know about you, but I find two-character indents quite unreadable when looking at the structure of a sketch. I use four for just about everything I do. The preceding first line, which was not changed, determines if the IDE will convert tab characters into spaces. When set to true, the IDE will convert tabs to spaces, while false will leave the tab characters as they are, unchanged.

This makes editing in the IDE a little more comfortable, in my opinion.

2.2 Globally Defined Paths

Before the various text files are read, the Arduino IDE defines some properties defining various paths and others for itself. These properties are global and can be used within any of the other configuration files, including your own. These globally defined properties are listed in Table 2-1.
Table 2-1

Globally defined properties

Property Name

Description

{runtime.hardware.path}

The absolute path of the hardware directory which is the folder containing the current platform.txt file.

{runtime.ide.path}

The absolute path of the directory where the arduino (or arduino.exe) application, the Arduino IDE, is found.

{runtime.ide.version}

The version number of the Arduino IDE as a valid number. Each component of the version number will be converted to use two digits. Then all the dots are stripped out, and finally, any leading zeros are removed leaving the final value. For example, the Arduino IDE version 1.8.5 will become “01.08.05” which becomes “010805” before finally being assigned as runtime.ide.version=10805. IDE versions prior to version 1.6.0 used a single digit for the IDE version number. For example, version 1.5.6 was 156 as opposed to 10506.

{ide_version}

Compatibility alias for runtime.ide.version.

{runtime.os}

The operating system that the IDE is currently executing on. The values are “linux”, “windows”, and “macosx”.

These global settings may be used in platform.txt, boards.txt, or, perhaps, but not very likely, programmers.txt. You may also use these paths in your amendments to the configuration files or in the various “local” versions that you create.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figg_HTML.gif Various configuration files can have a local version; boards.txt, for example, may have boards.local.txt. This local version allows you to make changes to the system configuration and not have to reconfigure every time the Arduino IDE is updated. Unfortunately, not all of the configuration files have a local version – programmers.txt is one that I have come across that doesn’t. See https://github.com/arduino/Arduino/issues/8556 for details, if you are interested.

2.3 Boards.txt

The $ARDINST/boards.txt file defines the various menu options for different types of microcontroller devices. These options either will appear on the Boards menu in the Arduino IDE or will be used when a specific board is selected from that menu. The file is read, and the various options are decoded and used by the IDE at startup. New boards can be added quite simply, if desired, by editing this file. Let’s look inside at the entry for the Arduino Uno.

2.3.1 Arduino Uno Example

The following is the complete listing of all entries for the Arduino Uno, in the IDE version 1.8.5 – other releases, both older and potentially newer, may be different:
uno.name=Arduino/Genuino Uno                ①
uno.vid.0=0x2341                                      ②
uno.pid.0=0x0043
uno.vid.1=0x2341
uno.pid.1=0x0001
uno.vid.2=0x2A03
uno.pid.2=0x0043
uno.vid.3=0x2341
uno.pid.3=0x0243
uno.upload.tool=avrdude                              ③
uno.upload.protocol=arduino
uno.upload.maximum_size=32256
uno.upload.maximum_data_size=2048
uno.upload.speed=115200
uno.bootloader.tool=avrdude                         ④
uno.bootloader.low_fuses=0xFF
uno.bootloader.high_fuses=0xDE
uno.bootloader.extended_fuses=0xFD
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.bootloader.file=optiboot/optiboot_atmega328.hex
uno.build.mcu=atmega328p                           ⑤
uno.build.f_cpu=16000000L
uno.build.board=AVR_UNO
uno.build.core=arduino
uno.build.variant=standard
  •     ①    Board name.

  •     ②    This section defines identification settings used to determine the board’s identity when it is plugged into the USB port on your computer.

  •     ③    These settings define parameters used for uploading compiled code to the board.

  •     ④    Bootloader settings are listed in this section.

  •     ⑤    Various build options are specified here.

The Arduino Wiki at https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification mentions, at least for IDE version 1.5.3 which appears to be the most recently documented, that

This file contains definitions and meta-data for the boards supported. Every board must be referred through its short name, the board ID. The settings for a board are defined through a set of properties with keys having the board ID as prefix.

What it doesn’t mention is how the system is supposed to know that “uno”, for example, refers to the Arduino/Genuino Uno device.

From the preceding listing, it is pretty obvious that the Uno’s short name must be “uno” as that is the prefix in use for every entry in this section of the file.

2.3.1.1 Board Identifier

The name parameter here identifies the board and defines what will be displayed in the Boards menu in the IDE:
uno.name=Arduino/Genuino Uno

2.3.1.2 Identification Settings

This section’s settings help to identify a genuine Arduino Uno. When you plug a device into a USB port, the device is queried to obtain a vendor and product identifier. This helps the system load the correct drivers (mainly Windows) or, on the very first time, to prompt you to load the appropriate drivers for the device. For the Uno, the following four pairs of vendors and product identifiers are known to be genuine:
uno.vid.0=0x2341
uno.pid.0=0x0043
uno.vid.1=0x2341
uno.pid.1=0x0001
uno.vid.2=0x2A03
uno.pid.2=0x0043
uno.vid.3=0x2341
uno.pid.3=0x0243
In the settings:
  • Vid is the vendor identifier.

  • Pid is the product identifier for the specific vendor.

From the preceding text, we can clearly see two vendors – “0x2431” and “0x2A03” – and the appropriate product identifiers to suit each vendor. Bear in mind that it isn’t necessarily the actual manufacturer of the Arduino board that is being identified; it is most likely to be the chip that converts the data on the USB port into the correct format for the microcontroller. Some Uno boards have another AVR microcontroller taking care of the communications, while others have an FTDI chip – both will register as different pids.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figh_HTML.gif Genuine boards, such as my own Duemilanove, which use an FTDI chip for communications, will not necessarily be recognized as the correct board. This is due to the FTDI chip which uses a generic pid and vid and is used by numerous different boards. However, this is nothing to worry about.

2.3.1.3 Upload Settings

When you click the upload button in the IDE, the settings defined in this section of the boards.txt file are used to set various parameters as desired, to enable proper communication with the currently chosen board:
uno.upload.tool=avrdude
uno.upload.protocol=arduino
uno.upload.maximum_size=32256
uno.upload.maximum_data_size=2048
uno.upload.speed=115200

To specify the tool to be used to carry out the upload, the upload.tool parameter is used. In this example, the tool in use is the program named avrdude. This tool is installed at the same time as the Arduino IDE.

The communications protocol to be used when uploading is defined in the upload.protocol parameter, while the maximum Flash and Static RAM (SRAM) sizes for the particular AVR microcontroller in use are defined in upload.maximum_size and upload.maximum_data_size parameters, respectively.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figi_HTML.gif The maximum size of an ATmega328P’s Flash RAM is 32,768 bytes, so why does upload.maximum_size only allow 32,256 bytes? It’s because the remaining 512 bytes are used for the bootloader. The Optiboot bootloader is 512 bytes in size, so that amount of Flash RAM needs to be reserved from the maximum available.

Actually, the Optiboot bootloader is only 500 bytes in size. You can see this when you look at the start and end addresses in the compilation listing file, $ARDINST/bootloaders/opitiboot/optiboot_atmega328.lst, which are 7E00hex and 7FF3hex, respectively. Subtracting gives 1F3hex which is 499decimal, but we need to add one because we started counting from zero.

Communications will be carried out at the baud rate specified in upload.speed. For this Uno example, that will be at 115,200 baud.

2.3.1.4 Bootloader Settings

This section of the boards.txt file defines various parameters to be used when you choose Burn bootloader from the IDE menu.

It should be obvious (shouldn’t it?) that burning a bootloader will require an In-Circuit System Programmer (ISCP) device as the AVR microcontroller you are burning a bootloader into doesn’t yet have a bootloader to allow uploading via the normal USB connection to the board!

../images/494109_1_En_2_Chapter/494109_1_En_2_Figj_HTML.gif You should be very careful to ensure that you have selected the correct board when burning a bootloader – on a good day, it will simply fail to work. On a bad day, it will set the fuses to something that might cause you some grief trying to unravel and get reprogrammed. On a really bad day, it could convert your prized Arduino board into something resembling a brick.

Okay, it’s probably not that bad, but you might end up with a need to purchase a new ATmega328P, and hopefully, it will be one that comes complete with an Uno bootloader burned in. Otherwise, you’ll have to do the bootloader burning exercise all over again.

Yes, I admit it. I did brick an Arduino board, so that’s how I know. It was a Digispark board with an ATtiny85 microcontroller, but I bricked it anyway! Investigation showed that I set the fuse to disable the RESET pin so that it could be used as a normal I/O pin. No amount of programming with a high-voltage programmer would rescue it, so there must have been some other settings that I broke as well.

I still have the device, somewhere, and one day, I will find out what I did wrong and, hopefully, fix it. Perhaps.

Continuing to look at the standard settings for an Arduino Uno, we can see the following settings:
uno.bootloader.tool=avrdude
uno.bootloader.low_fuses=0xFF
uno.bootloader.high_fuses=0xDE
uno.bootloader.extended_fuses=0xFD
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.bootloader.file=optiboot/optiboot_atmega328.hex

To specify the tool to be used to carry out the upload, the bootloader.tool parameter is defined. In the case of the Uno we are looking at here, the tool in use is the program named avrdude – the same as in the preceding text for uploading compiled sketches.

As described in Chapter 7, Section 7.1, “ATmega328P Fuses,” the AVR microcontroller has a number of fuses that can be utilized to set various configurations of the AVR microcontroller itself. The bootloader.low_fuses, bootloader.high_fuses, and bootloader.extended_fuses parameters define the required hardware settings for the microcontroller on the board.

Finally in this section, the bootloader.file parameter defines which of the many bootloaders supplied with the IDE is to be used for this board. The Uno uses the file optiboot/optiboot_atmega328.hex which is to be found in the $ARDINST/bootloader/ directory.

You can, if you wish, change the bootloader by either editing the boards.txt file to change the appropriate parameter or duplicating an existing section and changing the bootloader. The latter option is preferred. It’s worth bearing in mind that any updates to the IDE will most likely overwrite your changes to boards.txt, so how do we avoid this problem?

2.3.1.4.1 Boards.local.txt
Since release 1.6.6 of the Arduino IDE, a new file has been introduced, boards.local.txt , in which you can define various parameters that you wish to use instead of those in the boards.txt file. To continue the preceding example of changing the bootloader, you could create the file, if it doesn’t exist, and add the following to it:
uno.bootloader.file=my_new_bootloader/my_new_bootloader_atmega328.hex
This assumes that you won’t need any additional Flash RAM space for the bootloader over and above that required by the current bootloader. If you do, then add the following as well:
uno.upload.maximum_size=<what ever is required>

2.3.1.5 Build Settings

uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.board=AVR_UNO
uno.build.core=arduino
uno.build.variant=standard

The build.mcu setting defines the name of the microcontroller for this particular board. For the Uno, only an ATmega328P is defined. For other boards, the Nano, for example, there are two different microcontrollers available, the ATmega168 and the ATmega328P. Within each of those two boards, there are two different configurations, and the boards.txt has entries for each variant with global settings for all Nanos as well as the specific settings for the different microcontroller boards and the variants thereof.

The parameter build.f_cpu defines the system clock (CLKcpu) for the board. The Uno has a 16 MHz crystal installed, so that’s the speed that is defined in this example. This setting is used in your sketches, although you won’t actually see it, as the F_CPU variable is used, for example, if calculating the desired baud rate when using the Serial interface.

The build.board property is used to set a compile-time variable ARDUINO_{build.board} to allow the use of conditional code between #ifdefs in sketches and/or header files. The Arduino IDE automatically generates a build.board value if not defined. In this example, the variable defined at compile time will be ARDUINO_AVR_UNO.

To determine which file path is to be used when the compiler is looking for various files, main.cpp, for example, the build.core setting is used. The parameter is used to build a path to the files in $ARDINST/cores/<uno.build.core>/, which, for the Uno in this example, will be $ARDINST/cores/arduino/.

The variant of the board is then defined using the build.variant setting. This is used to build a path to the files that live in $ARDINST/variants/<uno.build.variant>/ and is where you will find the file named pins_arduino.h which defines any variations over the standard settings that apply to this particular board. For this example of an Uno, the path defined will be $ARDINST/variants/standard/.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figk_HTML.gif The IDE defines a number of global settings for the various paths to the cores and variants. These are available in other configuration files, but they don’t have the board’s prefix, so uno.build.core would correspond to the IDE’s global setting of build.core. If the board doesn’t specify a setting, the global one will be used; however, where a board does have an appropriate setting, that will override the global one created by the IDE, when the appropriate board is selected from the Boards menu in the IDE.

You can see some of these global settings in the file platform.txt.

2.3.1.6 Configuring an ICSP

If you always want to use an ICSP (In-Circuit System Programmer) to program a particular board, you can add the following line to the $ARDINST/boards.txt or $ARDINST/boards.local.txt file, probably as part of the build settings as detailed earlier, for example:
uno.upload.using=USBtinyISP

The name you use here is one of the ones that are to be found in the $ARDINST/programmers.txt file which is itself described later in this chapter, in Section 2.5, “Programmers.txt.”

You should make this change while the IDE is closed. When you next open the IDE, any time you select the Uno device as your board, it will automatically select the USB Tiny ISP device, in this case, to perform the uploads, rather than the bootloader.

If you wish to make this change as the default for all boards, then you should edit the preferences.txt file, as documented in Section 2.1, “Preferences.txt,” earlier in this chapter.

2.4 Platform.txt

The $ARDINST/platform.txt file defines platform-specific features and command-line tools, where libraries live and what they are called, and so on. It contains the various recipes used by the IDE in order to compile, build, upload, and/or program various devices and boards according to their different needs.

What is a platform? Well, in the case of the ATmega328P, or other AVR microcontrollers, the platform defines all the tools, compilers, linkers, command lines to be used and so on, for Atmel AVR microcontrollers. Other non-AVR microcontroller boards will have their own platform to define the specific tools and others for that particular microcontroller. Arduino boards with, for example, an ARM chip on them will use a different platform from those with the AVR microcontrollers.

Using this method allows for a fairly simple manner in updating the system to cope with new boards.

The Arduino system requires that this file define the following meta-data:
name=<platform name>
version=<platform version>

The name will be shown in the Tools ➤ Boards menu of the Arduino IDE, in grayed-out text, above the list of boards that conform to this particular platform. According to the documentation on the Arduino web site at https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification, the version is currently unused and is reserved for future use.

For the Arduino IDE version 1.8.5, we see this at the top of the file:
name=Arduino AVR Boards
version=1.6.22

Obviously, the version number of the platform can, and does, differ from the version of the IDE. Don’t be confused if you see something different.

2.4.1 Build Recipes

The platform.txt file, as mentioned earlier, contains a large amount of meta-data that configures the IDE to be able to compile sketches and upload them, among other things, for the Arduino boards running with AVR microcontrollers. It does this using recipes. Having different recipes for all the different platforms allows the IDE to be used for a myriad of different devices.

When you select a build in the IDE, a small number of settings are created automatically for you. These are as follows:
  • build.path – The path to the temporary folder to store various files created by the build process.

  • build.project_name – The project (sketch) name.

  • build.arch – The microcontroller architecture, which in our case is “avr” but, depending on the board, may be “sam”, “arm” and so on. The IDE gets this from the paths to $ARDINST.

    On my system, $ARDINST is defined as $ARDBASE/hardware/arduino/avr – and the path portions define the hardware folder location, $ARDBASE/hardware; then the vendor name, arduino; and finally the architecture, avr for boards with the ATmega328P. For Arduino SAM boards, the path would be $ARDBASE/hardware/arduino/sam instead. The final part of the path gives the build.arch name.

A number of additional settings are defined within the $ARDINSToards.txt file based on the particular board chosen on the Tools ➤ Boards menu – see Section 2.3, “Boards.txt,” for those details – and the IDE global variables can be used within this file too. You can find more details on those variables in Section 2.2, “Globally Defined Paths.”

The compilation process can read source files written in plain C – these are the .c source files, C++ (.cpp), or even assembly language (.S). It has to know how to convert these files into object files (.o) which can be gathered together by the linker to create an executable file. The way this happens is by using the recipes within the platform.txt file.

The recipes are variables in the format
recipe.<source_format>.o.pattern
And the “source_format” is simply the file extension for the files in question. This gives us the following variables:
  • recipe.c.o.pattern – To convert C files to object files

  • recipe.cpp.o.pattern – To convert C++ files to object files

  • recipe.S.o.pattern – To convert assembly language files to object files

You will notice, I hope, that the source format in each is case sensitive. Assembly language files must have an upper case .S extension.

Taking one of them as an example, this is what I found in my platform.txt file for the Arduino IDE version 1.8.5:
## Compile c files
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags}
-mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version}
-DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags}
{build.extra_flags} {includes} "{source_file}" -o "{object_file}"
If we break the preceding recipe down into its constituent parts, we see the following variables and command-line options:
  • "{compiler.path}{compiler.c.cmd}" defines where the compiler tool can be found – {compiler.path} – and what it is called, {compiler.c.cmd}. The use of double quotes allows for spaces and other non-alphanumeric characters in the path or command name.

    In the IDE, I see that {compiler.path} is defined as {runtime.tools.avr-gcc.path}/bin/; and as you can see, these recipes can refer to other variables defined in this file or elsewhere. The {compiler.c.cmd} is defined as avr-gcc, and this is not actually the compiler, but a front end to all phases of the compilation process and which can be used to control the whole process.

  • {compiler.c.flags} defines a list of flags and options to be passed to the {compiler.c.cmd} utility to define how the build should progress, what outputs are required and so on. There are numerous options in IDE 1.8.5; but one in particular, -c, tells the compiler front end to stop compiling after the object file has been created and not to run the link phase.

  • -mmcu={build.mcu} defines another compiler option. It tells the C compiler which microcontroller is in use on the board. It comes from $ARDINST/boards.txt and, for the Uno, is defined as uno.build.mcu=atmega328p. The name part, uno, is stripped off first.

  • -DF_CPU={build.f_cpu} is very useful when writing code for multiple boards. The speed of the AVR microcontroller clock is defined in the compilation process, as opposed to being hardcoded in the actual source files, for example, #define F_CPU 16000000L. This would require editing before running on a board with a different clock speed.

    The variable is defined in $ARDINST/boards.txt and, for the Uno, is uno.build.f_cpu=16000000L. The name part, “uno.”, is stripped off first.

  • -DARDUINO={runtime.ide.version} defines a numeric value for the Arduino IDE version in use. It is created automatically by the IDE and is described in Section 2.2, “Globally Defined Paths.” For IDE version 1.8.5, for example, it becomes 10805.

  • -DARDUINO_{build.board} references the uno.build.board=AVR_UNO variable from the $ARDINST/boards.txt – the example shown is, once more, for the Uno. This can be used in conditional code to determine the board in use and, from that, whether certain features are available or otherwise. In this example, it would define a variable named ARDUINO_AVR_UNO.

  • -DARDUINO_ARCH_{build.arch}, as described earlier, defines the architecture we are building for. For the purposes of this book, this will be “avr” giving ARDUINO_ARCH_avr.

  • {compiler.c.extra_flags} are some additional flags that you or I can define in $ARDINST/platform.local.txt to be added to the command line for this recipe. By default, these are blank.

  • {build.extra_flags} are some additional flags that you or I can define in $ARDINST/boards.local.txt to be added to the command line for this recipe. By default, these are blank.

  • {includes} is the list of paths that the compiler will use to search for files #included in the various source files. The format is -I/include/path and so on. You can have more than one path. The documentation online has this to say:

    Note that older IDE versions used the recipe.preproc.includes recipe to determine includes, which is undocumented here. Since Arduino IDE 1.6.7 (arduino-builder 1.2.0) this was changed and recipe.preproc.includes is no longer used.

    This is not really very helpful, as includes remains undocumented, and even the “no longer used” recipe recipe.preproc.includes actually has {includes} as part of its definition.

  • "{source_file}" is the path to the single source file being compiled. The double quotes allow for spaces and other non-alphanumeric characters in the file name.

  • -o "{object_file}" is the path to the single object file which will be created by the compilation phase. The double quotes allow for spaces and other non-alphanumeric characters in the file name.

2.4.2 Pre- and Post-build Hooks

Pre- and post-build hooks were introduced in Arduino version 1.6.5 and are found in the $ARDINST/platform.txt file. In version 1.8.5, the following hooks are available for your use:
  • recipe.hooks.sketch.prebuild.NUMBER.pattern – Called before sketch compilation

  • recipe.hooks.sketch.postbuild.NUMBER.pattern – Called after sketch compilation

  • recipe.hooks.libraries.prebuild.NUMBER.pattern – Called before libraries compilation

  • recipe.hooks.libraries.postbuild.NUMBER.pattern – Called after libraries compilation

  • recipe.hooks.core.prebuild.NUMBER.pattern – Called before core compilation

  • recipe.hooks.core.postbuild.NUMBER.pattern – Called after core compilation

  • recipe.hooks.linking.prelink.NUMBER.pattern – Called before linking

  • recipe.hooks.linking.postlink.NUMBER.pattern – Called after linking

  • recipe.hooks.objcopy.preobjcopy.NUMBER.pattern – Called before objcopy recipe execution

  • recipe.hooks.objcopy.postobjcopy.NUMBER.pattern – Called after objcopy recipe execution

  • recipe.hooks.savehex.presavehex.NUMBER.pattern – Called before savehex recipe execution

  • recipe.hooks.savehex.postsavehex.NUMBER.pattern – Called after savehex recipe execution

These are identified by the recipe.hooks part. The next part determines which stage in the compilation the hook will be called. Prexxxxx and postxxxxx indicate that the pattern will be called before the appropriate stage or afterward.

In order that multiple hooks can be called at any stage, the NUMBER part is a sequence number which should be 1, 2, 3, 4, and so on – there’s one number for each hook to execute at a given stage in proceedings. The end of the recipe is always the word pattern.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figl_HTML.gif If you find that you require ten or more hooks, then your NUMBER parts should be 01, 02, ..., 10, 11, and so on.

Following the equals sign are the commands you want to execute. For example, in $ARDINST/platform.local.txt, you could add the following on Linux:
recipe.hooks.sketch.prebuild.1.pattern=echo Compiling sketch: {build.source.path}
recipe.hooks.sketch.postbuild.1.pattern=echo Compiled
Or the following on Windows:
recipe.hooks.sketch.prebuild.1.pattern=cmd /C echo Compiling sketch:
{build.source.path}
recipe.hooks.sketch.postbuild.1.pattern=cmd /C echo Compiled"

I noticed that some, but not all, variables do not get expanded. Using "{source_file}", for example, doesn’t expand, but {build.source.path} does. Also, the entire text after the equals sign becomes part of the message, not just the command’s output. The preceding code, on Linux, displays the text “echo Compiling sketch: /full/path/to/my/sketch/here” rather than just “Compiling sketch:

/full/path/to/my/sketch/here”. A similar thing happens with Windows.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figm_HTML.gif The command used cannot be a built-in command and must be found on your system’s path ($PATH on Linux and MacOS, %path% on Windows.)

On Windows, the echo command is a built-in command. It cannot be found on %path% when the compilation is started, so the whole compilation fails because echo can’t be found. This is because of the way that the Java command exec, from Runtime.getRuntime(), works.

So how did the preceding echo command work for Windows? I created an echo.bat file and put it on my Windows %PATH%.

Commands to be executed in the hooks must be found on the $PATH; if not, they will not be executed, and the recipe will fail.

2.5 Programmers.txt

The programmers.txt file is very much the least documented of the various text files used by the Arduino IDE. The Wiki pages describe the other files, but nothing at all, other than a brief mention of its name, for programmers.txt.

It is assumed, possibly incorrectly, that people creating and building new ICSP devices know what all the parameters mean and will supply a list of required entries, for their device, to be added to programmers.txt.

It is also likely that whenever anyone creates or updates a programming device, or settings, it would be submitted to the Arduino maintainers for inclusion in the next release of software.

../images/494109_1_En_2_Chapter/494109_1_En_2_Fign_HTML.gif The programmers.txt file will be overwritten by each new IDE update, so if you have made any changes, you really should keep a record of them prior to upgrading.

$ARDINST/programmers.txt holds details about the various programming devices that the Arduino IDE can use to upload code to your Arduino board. Unlike boards.txt and platform.txt, the IDE doesn’t seem to recognize a local variant, programmers.local.txt, even if one exists. Therefore, any changes that you make to your own installation will need to be made to the supplied $ARDINST/programmers.txt file, and this will be overwritten when the IDE is upgraded.

As this is a bit of a nuisance, I logged issue 8556 about it at https://github.com/arduino/Arduino/issues/8556. It could be that this is by design and not an actual problem. We shall see what transpires.

The file contains parameters that are relevant to the various programming devices that can be used, and depending on the settings, these may appear in the various menu options under the Tools menu in the IDE.

An example of an entry in the file is as follows:
usbtinyisp.name=USBtinyISP
usbtinyisp.protocol=usbtiny
usbtinyisp.program.tool=avrdude
usbtinyisp.program.extra_params=
This is for the “USB Tiny” ICSP (In-Circuit System Programmer) and shows the following:
  • The device name, as it will appear in the Tools ➤ Programmer menu. In this case, it is “USBtinyISP.” You can change this if you prefer to use a different name.

  • The protocol to be used when executing the IDE option to “Upload using programmer.”

  • The tool that will be used when uploading. Here we can see that it is defined as using the avrdude utility.

  • Any extra parameters that may be needed to do the upload. In this example, there are none. However, if any were needed, they would be required to be consistent with the syntax of the programming tool in use.

If, for example, the device required a serial port to be used for the upload, then you could add the following:
usbtinyisp.program.extra_params=-P{serial.port}

This would allow the command line passed to avrdude to be supplied with the -P option, to select a serial port, and it would be set to the value chosen in the IDE on the Tools ➤ Port menu option.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figo_HTML.gif This is obviously just an example; the USB Tiny device doesn’t need a serial port.

2.6 Compiling a Sketch

When you open a project in the Arduino IDE, you will notice that all files in the project directory with an .ino, .h, .c, or .cpp extension get placed on a tab of their own. These are assumed to be all the source files that make up your project. You can, if you wish, open other files within the IDE, but these will not automatically open in separate tabs when you subsequently reopen the project. They will have to be manually opened if editing or viewing is required.

You should also be aware that there is not a function called main() in any of the files open in the project. Anyone who has programmed in C or C++ will know that main() is the program’s entry point. What’s going on?

The Arduino IDE supplies its own main() function, so that you don’t have to. In order to make life easier for budding microcontroller makers and developers, the Arduino system hides a lot of stuff from you. I’ll be taking a look at the main() function soon.

When you compile a project in the Arduino IDE, a number of things take place, and these separate processes are described in the remainder of this chapter. Your sketch will be converted into a C++ file by the Arduino Preprocessor.

2.6.1 Arduino Sketch (*.ino) Preprocessing

An Arduino sketch is a very much simplified C++ source file which may be composed of many files with the extension .ino and, occasionally, some additional files with the extension .cpp. To convert the sketch into a valid C++ file, a number of actions are carried out:
  • Maybe create a temporary compiler working folder in the system’s main temporary folder or directory. This will be within /tmp on Linux and something along the lines of c:users<your_name>AppDataLocalTemparduino_build_<some_number> on Windows. This happens only on the very first compilation of this particular sketch. For the rest of this discussion, I shall refer to this folder as $TMP.

  • If your sketch is composed of a number of .ino files, those files are concatenated into a single .ino.cpp file, in the $TMP/sketch subfolder, starting with the main sketch file which is the .ino file with the same name as the sketch’s folder name. The remainder of the .ino files are appended to the end of the main one, in alphabetical order. If your sketch was named Blink.ino, then the generated file would be named $TMP/sketch/Blink.ino.cpp.

  • The line #include <Arduino.h> is added at the beginning of the .ino.cpp file, if not already present.

  • All libraries used in the sketch are detected, and the include paths for those libraries are discovered. This is done by running a dummy compilation with the output being discarded (to the null device on Windows and /dev/null on Linux) and processing any relevant error messages.

  • Prototypes for all functions in the .ino.cpp file are generated. If, as occasionally happens, a valid function prototype cannot be automatically generated, you will need to add one explicitly to the .ino file that defines the failing function.

  • The .ino.cpp file is processed so that there are relevant compiler preprocessor #line and #file directives so that error reporting will be accurate and refer to the correct lines in the correct source files, as opposed to referencing the lines within the concatenated .ino.cpp file.

These actions are performed by the arduino-preprocessor tool which lives on GitHub at https://github.com/arduino/arduino-preprocessor.

2.6.2 Arduino Sketch (*.ino) Build

After preprocessing, the build is then completed by the arduino-builder tool, found at https://github.com/arduino/arduino-builder, which
  • Compiles the .ino.cpp file, created by the preprocessing stage, into a module with a .ino.cpp.o extension. This module file is stored in the $TMP/sketch subfolder created by the Arduino Preprocessor tool described earlier.

  • Compiles all other .c or .cpp files, including main.cpp, into separate modules in the $TMP/sketch subfolder.

    ../images/494109_1_En_2_Chapter/494109_1_En_2_Figp_HTML.gif If the sketch’s configuration – the board and so on – has not changed since the previous compilation, then some of these modules may be reused rather than recompiled. This saves time on the second and subsequent compilations. This is only done if the source file(s) for the module to be reused has not been edited or changed of course.

  • Any libraries used by the sketch will be compiled as separate modules too. Once again, these will be written as .o files in the $TMP/libraries subfolder.

  • The Arduino core files are compiled as .o files into $TMP/core. These core files are the like of wiring_analog.c, wiring_digital.c and so on, as installed under the IDE.

  • The individual core modules (*.o) are then built into a single static library, core.a, in the $TMP/core subfolder – for example, on Windows, c:users<your_name>AppDataLocalTemparduino_build_<some_number>corecore.a.

  • After all the modules have been created, the linker combines them all into a single elf format binary file. This file lives in the main temporary folder created earlier and will be named $TMP/<sketch_name>.ino.elf, $TMP/Blink.ino.elf, for example.

  • The $TMP/<sketch_name>.ino.elf file is then used to create a file named $TMP/<sketch_name>.ino.eep which contains data to be written to the AVR microcontroller’s EEPROM area.

  • The $TMP/<sketch_name>.ino.elf file is also used to create a file named $TMP/<sketch_name>.ino.hex which contains the code used to flash the AVR microcontroller with your sketch. This code is in “Intel Hex” format.

This ends the compilation process. If the upload button is clicked in the IDE, rather than the compile (or verify) button, then the $TMP/<sketch_name>.ino.hex file is uploaded to the AVR microcontroller, using an Arduino-specific version of the avrdude tool which can be found on GitHub at https://github.com/arduino/avrdude-build-script.

There’s a menu option, Sketch ➤ Export Compiled Binary, which will export the compiled hex files to the sketch’s folder. This could be used for passing copies of your application for your friends to upload, without letting them see your source code. There are two files created:
  • Sketch_name.ino.standard.hex – This is the hex file to upload your code and only your code.

  • Sketch_name.ino.with_bootloader.standard.hex – This file, if uploaded, will write both the bootloader and your application code.

If you have a bootloader installed on your ATmega328P, then you can use it to upload the files using avrdude. Normally, when using an ICSP to program your device, the bootloader will be overwritten when the chip is wiped. However, if you use the ICSP to upload the preceding file with the bootloader, then you effectively burn a bootloader as well as your application’s code into the Arduino board.

Either file can be uploaded using the bootloader – if it is still installed on your device – and after doing so, regardless of which of the two files you upload, the bootloader will still be present afterward.

All this “just works” and it makes life easy; however, what is the Arduino system hiding from you?

../images/494109_1_En_2_Chapter/494109_1_En_2_Figq_HTML.gif You can see all of this happening, before your very eyes, if you edit the preferences in the IDE to show verbose compiling and/or upload messages.

The following chapters describe the various files that your sketch ends up including when the compilation process has completed.

2.7 The Arduino main() Function

As previously noted, the main() function is where all C or C++ applications start executing. As an Arduino developer though, you don’t have to supply one as the system does it for you. The Arduino main() function in version 1.8.5 is found in the file $ARDINC/main.cpp beneath the Arduino installation directory and is shown in Listing 2-1.
#include <Arduino.h>                                       ①
// Declared weak in Arduino.h to allow user redefinitions.
int atexit(void (* /*func*/ )()) { return 0; }
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }
void setupUSB() __attribute__((weak));
void setupUSB() { }
int main(void)
{
    init();                ②
    initVariant();         ③
#if defined(USBCON)        ④
    USBDevice.attach();
#endif
    setup();               ⑤
    for (;;) {             ⑥
        loop();
        if (serialEventRun) serialEventRun(); ⑦
    }
    return 0;
}
Listing 2-1

The Arduino main() function

  •     ①    The first point to note is the inclusion of the file Arduino.h (found in the folder $ARDINC), and this is where numerous constants and other definitions specific to the Arduino are declared. If you look at this file, it makes interesting reading; there are numerous tests to determine which board is in use and which features of the AVR microcontroller can be used. The Arduino.h header file is described in Section 2.8, “Header File Arduino.h.”

  •     ②    Within the main() function itself, there is a call to init() which is found in $ARDINC/wiring.c. This initializes a whole raft of features for the Arduino and carries out this based on the actual microcontroller in use on the board. If you decide to dive into this function, make sure that you are armed with a copy of the data sheet for your specific microcontroller; otherwise, nothing much will make sense.

  •     ③    The next function call, initVariant(), carries out any special initialization for boards that are possibly not covered by the standard initialization. The function defaults to doing nothing (you can see it at the top of main.cpp); but as it is declared as weak, it can be overridden, as required, in a sketch.

  •     ④    Some boards like the Leonardo use USB for serial communications and thus require USB setting up, so there is a test to see if this is required. If so, then the USBDevice.attach() function is called to do the needful.

  •     ⑤    The sketch’s setup() function is called next. This is where the sketch’s own initialization gets carried out.

  •     ⑥    After the call to setup(), an endless loop is entered where the sketch’s loop() function is called once on every pass through the loop.

  •     ⑦    The function serialEventRun() is called each time through the loop as well. This in itself calls out to another (weak) function named serialEvent(), if it exists in the sketch, and this is used to collect up any data that has been received into the Serial input buffer but not yet read by the sketch.

  • There is an example of its use on the Arduino Tutorials web site at www.arduino.cc/en/Tutorial/SerialEvent.

This is how the sketch’s ino file fits into the real world: the setup() function is called once, and the loop() gets called repeatedly until the Arduino runs out of power or is turned off.

Calling the loop() function many times in this manner will impart some overhead to each execution. There is the stack frame setup prior to the function call and the stack teardown at the end prior to the function returning. These housekeeping instructions take time to execute and can slow down your code. You can avoid this by defining your own loop with a for, while, or do block within the loop() function, so that loop() only ever gets called once and never has to return to main(). Listing 2-2 shows a brief example.
void loop() {
    while (1) {
        // Do your loop code here.
    }
}
Listing 2-2

A “never returning” loop() function

../images/494109_1_En_2_Chapter/494109_1_En_2_Figr_HTML.gif Bear in mind that if you do decide to create your own loop in the manner described earlier, you might cause problems with any serial communications that would have been processed by the call to serialEventRun() in the main() function.

Check the documentation on the Arduino Tutorials web site at www.arduino.cc/en/Tutorial/SerialEvent to be sure that you will not be causing yourself any worries.

2.8 Header File Arduino.h

As mentioned previously, when you compile a sketch in the Arduino IDE, there is a certain amount of reorganization taking place to convert your sketch into something resembling a proper C/C++ program. The file Arduino.h , which can be found in $ARDINC as can all the other Arduino-specific header files, is included at the top of the converted source code. It is in, or from, this file that much of the initialization of a sketch takes place.

The following list outlines the actions of the Arduino.h file:
  • Various standard C/C++ header files are included. I will not be discussing those here.

  • A number of AVR-specific header files are included from the AVRLib sources, in $AVRINC. These are as follows:
    • avr/pgmspace.h

    • avr/io.h

    • avr/interrupt.h

  • A strange header file, binary.h, is included next.

  • All the Arduino-specific function headers are defined, for example, pinMode(), digitalWrite() and so on, along with a number of useful constants such as HIGH, LOW, INPUT, OUTPUT amongst others.

  • If the compilation is using the C++ compiler (avr-g++) as opposed to the C compiler (avr-gcc), then
    • WCharacter.h is included.

    • WString.h is included.

    • HardwareSerial.h is included.

    • USBAPI.h is included.

    • If the compiler discovers that the microcontroller in use has both hardware serial and CDC serial, then it cannot continue with the compilation, so an error is displayed and the compilation ends.

  • Finally, the header file pins_arduino.h is included.

The relevant header files are described in the following sections, as are any other headers that they themselves include.

2.8.1 Header File avrpgmspace.h

This header file is included from $AVRINC, to allow pins_arduino.h, as described in the following, to create lookup tables within the program space – in flash memory, as opposed to in the scarce Static RAM (SRAM) on the device. It defines a number of typdefs and functions to copy data between the program space, in flash, and the variable space in RAM. It doesn’t make for very interesting reading I’m afraid.

2.8.2 Header File avrio.h

This file, from $AVRINC, sets up all the AVR-specific stuff for the appropriate AVR microcontroller that is in use on the Arduino board. The settings you chose in the IDE for Tools ➤ Boards will determine the specific device file that will be included. In the majority of cases, and for our purposes here, this will be the ATmega328P, and so this file simply causes the AVR definitions for that microcontroller to be read in from the file avr/iom328p.h.

This file also includes sfr_defs.h to set up numerous macros for memory address simplification, some functions to handle looping until a bit is clear (or set) and so on. These are not discussed here.

Other files included by avr/io.h are as follows:
  • <avr/portpins.h>

  • <avr/common.h>

  • <avr/version.h>

  • <avr/xmega.h> (only if we are compiling for an XMega device, which we are not, so this header will not be discussed further)

  • <avr/fuse.h>

  • <avr/lock.h>

The AVRLib header files are to be found in $AVRINC, under your Arduino 1.8.5 installation, unless otherwise stated. These header files are not part of the Arduino IDE per se, but are supplied as part of the AVRLib, which the IDE uses.

2.8.2.1 Header File avr/iom328p.h

If you’ve ever looked at the data sheet for the ATmega328P, then you will notice that the various registers, and the bits thereof, have strange-sounding acronyms. This header file, from $AVRINC, is the one which creates all the constants so that you can refer to those acronyms in your code. In addition to these acronyms, various other constants are defined to manage RAM sizes, fuse bits, sleep modes, interrupt vectors and so on. This is another important header file, but it really doesn’t make for good bedtime reading.

If your Arduino board uses a different AVR microcontroller device, then a different iomxxx.h file will be included, rather than this one, so the definitions will be suitable for the board and/or microcontroller in use.

The exact file which will be included is defined by the IDE’s Tools ➤ Boards settings.

2.8.2.2 Header File avr/portpins.h

This header file, from $AVRINC, is a continuation of the device-specific avr/iom328p.h file and defines some additional constants which are common to all of the other devices. Some of the definitions in this file will not be relevant to all devices, but the code in this file does do some checks to see if a definition will be relevant, before defining it. It does this by checking for constants defined in the avr/iom328p.h header file and, if defined, sets up the additional constants.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figs_HTML.gif Obviously, if the board in use is not based on the ATmega328P, then the reference to avr/iom328p.h in the preceding text would of course be to a different header file for the device actually in use on the board.

2.8.2.3 Header File <avr/common.h>

According to the comments in this header, This [sic] purpose of this header is to define registers that have not been previously defined in the individual device IO header files, and to define other symbols that are common across AVR device families.

I think that about covers it!

The file can be found in $AVRINC.

2.8.2.4 Header File <avr/version.h>

This is a header file specific to the AVRLib code and not to the AVR devices. It defines various constants to indicate which version of the AVRLib is in use. For example, for a version 2.0.0 AVRLib, we see the constants shown in Listing 2-3.
#define __AVR_LIBC_VERSION_STRING__ "2.0.0"
#define __AVR_LIBC_VERSION__        20000UL
#define __AVR_LIBC_DATE_STRING__    "20150208"
#define __AVR_LIBC_DATE_            20150208UL
#define __AVR_LIBC_MAJOR__          2
#define __AVR_LIBC_MINOR__          0
#define __AVR_LIBC_REVISION__       0
Listing 2-3

AVRLib constants

You could use these constants to check whether a specific version of the library is in use and, from that, determine if some function can be used or otherwise.

Once again, the file can be found in $AVRINC.

2.8.2.5 Header File <avr/fuse.h>

Fuses are programmable bits in 1, 2, or 3 bytes inside the AVR microcontroller. These are used to set various features of the hardware and are covered in some detail in Chapter 7, Section 7.1, “ATmega328P Fuses.” The data sheet for the appropriate AVR device has full details, and warnings!

This header file, from $AVRINC, sets up a structure type (__fuse_t) that corresponds to the fuse bits for the appropriate board.

2.8.2.6 Header File <avr/lock.h>

This header file, from $AVRINC, sets up lock bit details for the specific AVR microcontroller in use. This will not be discussed further here, so see the data sheet for full details.

2.8.3 Header File avrinterrupt.h

Because the init() function, as described in the following, sets up Timer 0 with an interrupt routine to keep track of the number of milliseconds (millis) that have passed since a sketch started, this header file is required. In essence, and among many checks, it creates the ISR() macro which allows you to create interrupt handlers when using AVR-specific C/C++ code. The Arduino Language uses a slightly different system, attachInterrupt(), for example, for external interrupts. Arduino interrupts will be described in Chapter 3, Section 3.5, “Interrupts.”

This file is also part of the AVRLib and is found in $AVRINC.

2.8.4 Header File binary.h

This header file defines a constant for every numeric value from 0 through to 255, in the value’s binary number format. The constants defined, using #define, are of the format
#define B0 0
#define B00 0
#define B000 0
#define B0000 0
#define B00000 0
#define B000000 0
#define B0000000 0
#define B00000000 0
#define B1 1
#define B01 1
#define B001 1
#define B0001 1
#define B00001 1
#define B000001 1
#define B0000001 1
#define B00000001 1
...
...
#define B11111110 254
#define B11111111 255

It looks strange, yes? The header is defining as many different binary style constants for every number between 0 and 255. Why does 0 get so many different constants while 255 only has one? This allows the programmer to specify zero in as many ways as there are leading zeros in the binary representation of the number zero. This applies to all the numbers, but once you reach 128, there are no more leading zeros, so those values only have a single constant defined.

As I said, a little strange, but it allows you to write code such as
DDRD = B11110000;
Set the D port on the AVR microcontroller to have the top 4 bits as OUTPUT and the bottom 4 as INPUT, equivalent to the following Arduino code:
pinMode(7, OUTPUT);    // Port D, pin 7.
pinMode(6, OUTPUT);    // Port D, pin 6.
pinMode(5, OUTPUT);    // Port D, pin 5.
pinMode(4, OUTPUT);    // Port D, pin 4.
pinMode(3, INPUT);     // Port D, pin 3.
pinMode(2, INPUT);     // Port D, pin 2.
pinMode(1, INPUT);     // Port D, pin 1.
pinMode(0, INPUT);     // Port D, pin 0.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figt_HTML.gif Only one line of code? To do all that? Yes, one line of AVR code can correspond to numerous lines of Arduino code. This is another example of how the Arduino makes life easier for the beginner – which of the two preceding code sections is the easier to read and understand?

2.8.5 Header File WCharacter.h

This header file defines a number of inlined functions which can be used to determine if a character is numeric, alphanumeric, and so on. This is not specific to the Arduino software and will not be discussed further.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figu_HTML.gifWhat are inlined functions?

Inlined functions get copied verbatim into the code where they are called. Normally, functions are set up in the executable once and called from many places. Inlining them improves runtime efficiency but at the expense of code size.

Have a look at the header file, and if you are short of space in your device, try not to use the functions defined here too often; or if you have to use them, do something similar to the code in Listing 2-4.
// Define my own function to avoid too many copies of
// 'isAlphaNumeric()' which is always inlined.
// See 'WCharacter.h' for details.
boolean isAlphaNum(int c)
{
    return isAlphaNumeric(int c);
}
Listing 2-4

Avoiding inlined code to save space

Then call the isAlphaNum() function frequently from your own code, rather than calling the isAlphaNumeric() function frequently.

2.8.6 Header File WString.h

The WString.h header file defines a C++ class named String. I have to admit to not seeing any Arduino code that uses this class, but maybe I haven’t been reading enough code. As this isn’t specifically Arduino code, even though the class has been written for Arduino, it will not be discussed further.

Okay, I lied. String is used by the Serial class, but deep down. Serial inherits from Stream which inherits from Print, and Print uses String internally.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figv_HTML.gif Using this class will seriously increase the size of your sketches and may result in very difficult to diagnose runtime errors if too much dynamic memory is allocated – which this class does internally.

2.8.7 Header File HardwareSerial.h

This is the header file that defines the Serial interface whereby your Arduino board can talk back to your main computer over the USB cable. There’s a lot going on in this file, and it makes interesting reading to see how the Arduino library works.

If your device has less than 1024 bytes of RAM, then two buffers of 16 characters each are created. One is for serial receiving and the other is for serial transmission. The buffer sizes are increased to 64 characters if you have more than 1024 bytes of RAM available. The ATmega328P has 2048 bytes, so the larger-sized buffers are created.

The buffers are set up as what is known as circular buffers. They have a pointer to the first free character for insertion into the buffer and a pointer to the next character to be removed from the buffer. Hopefully, never the twain shall meet, but if they do happen to meet, the sketch will suspend for a while until some data has been removed from the buffer allowing the new data to be inserted.

Circular buffers are described in some detail at https://en.wikipedia.org/wiki/Circular_buffer.

Also set up in this file are a couple of interrupt routines which are called automatically by the AVR microcontroller whenever there is an empty transmit or a full receive buffer for the built-in USART device. The Serial class will, in the case of a transmission, read the next character from the Arduino transmit buffer and write it into the hardware register as appropriate to have it transmitted out via the USART. A similar process takes place for the receive interrupt. The hardware buffers on the AVR microcontroller are a single byte in size.

In the case where the AVR device has more than one USART, the Mega 2560 series, for example, then these are also set up from other header files included by this one. These additional hardware serial devices are not discussed further as the default Arduino board using the ATmega328P only has a single USART and they are all similar.

2.8.8 Header File USBAPI.h

This is a header specifically for devices which have built-in hardware USB features – these being boards based around the ATmega32U4 microcontroller which is on board the Arduino Leonardo, Pro Micro, Micro, and a few other models. As the ATmega328P doesn’t have hardware USB on board, this file will not be discussed any further.

2.8.9 Header File pins_arduino.h

The version of pins_arduino.h that is included is dependent on the Arduino board in use and, thus, the AVR microcontroller in use on that specific board. For the default board, the file is located in $ARDINST/variants/standard, using an ATmega328P, while the Adafruit Gemma board has its pins_arduino.h in $ARDINST/variants/gemma and uses an ATtiny85 device. The included file sets up the pin assignments for the appropriate device.

The board is chosen by the developer using the IDE’s Tools ➤ Boards menu option.

It is this header file that defines constants for the analogue pins, A0 which has the value 14 through A7 which is defined as 21, for example.

Also created within the program space on the AVR device are a number of small lookup tables that are used to
  • Convert an AVR port name (Px) to the Data Direction Register for that port (DDRx). This is table port_to_mode_PGM.

  • Convert an AVR port name (Px) to the output port register for that port (PORTx). This is table port_to_output_PGM.

  • Convert an AVR port name (Px) to the input port register for that port (PINx). This is table port_to_input_PGM.

  • Convert a digital pin number (0–13) to the AVR port (PORTB, PORTC, or PORTD). This is table digital_pin_to_port_PGM.

  • Convert a digital pin number (0–13) to the specific pin number (or bit number) on the AVR port (PORTB, PORTC, or PORTD). This is table digital_pin_to_bit_mask_PGM. The entry stored in this table is a bitmask with only 1 bit set, the bit that corresponds to the pin number.

  • Convert a digital pin number (0–13) to one of the timer outputs on the device (6 on the standard Arduino board). This is table digital_pin_to_timer_PGM and is used in the analogueWrite() function for pulse width modulation (PWM).

2.9 The init() Function

This function is located within the file $ARDINC/wiring.c.

At the start of every Arduino sketch, the init() function is responsible for
  • Enabling the global interrupt flag

  • Configuring Timer/counter 0 to provide PWM on pins D5 and D6 and initiating the millis() counter facility by setting up the Timer/counter 0 Overflow interrupt handler

  • Configuring Timer/counter 1 to provide PWM on pins D9 and D10

  • Configuring Timer/counter 2 to provide PWM on pins D3 and D11

  • Initializing the Analogue to Digital Converter

  • Disabling the USART from pins D0 and D1

2.9.1 Enabling the Global Interrupt Flag

The function init() begins by enabling interrupts globally as shown in Listing 2-5. Arduino boards require interrupts to be enabled so that the millis() function can begin counting, once the appropriate timer (Timer/counter 0) is configured and started.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figw_HTML.gif In the following walk-through of the source code for the init() function, and as with many other code listings in this book, the code that is not relevant to the ATmega328P has been removed to reduce the amount of source code listed and to avoid confusing this author!
void init()
{
    // this needs to be called before setup() or some
    // functions won't work there
    sei();                               ①
Listing 2-5

Setting interrupts on

    ①    Turns on global interrupts. This is required to make functions such as millis() and micros() work.

The code continues to enable Timer/counter 0 next.

2.9.2 Enabling Timer/counter 0

Timer/counter 0 is used to count the milliseconds which have passed since the sketch began operating after power on, system reset, or uploading a sketch. It does this by setting up the following for Timer/counter 0:
  • The prescaler for Timer/counter 0 is set to divide the system clock (16 MHz) by 64 so that every 64 ticks of the system clock, the timer/counter’s own clock will tick once and increment the counter value by 1. As this is an 8-bit timer, it can only count from 0 to 255 and then roll over, or overflow, to 0 again and so on. The overflow will occur every 256 timer/counter clock ticks which equates to 64 * 256 system clock ticks.

      1/ (CPU Frequency / prescaler) * Timer ticks until overflow
    = 1/(F_CPU / 64) * 256
    = 1/(16000000 / 64) * 256
    = 1/250000 * 256
    = 4 microseconds * 256
    = 1024 microseconds
    = 1 millisecond plus 24 microseconds.

    The interrupt takes account of those extra 24 microseconds and will adjust the millis() result to account for them whenever they accumulate enough to add an extra millisecond to the timer.

  • The interrupt on Timer/counter 0 overflow is set up and enabled. The interrupt will fire every time the timer/counter’s value overflows from 255 to 0. The Timer/counter 0 Overflow interrupt will update the millis counter once every 256 timer clock ticks (256 Timer/counter 0 clock ticks.) This is calculated as

Timer/counter 0 is also used to provide 8-bit PWM (pulse width modulation for analogueWrite() ) on pins D5 and D6.

The init() function code walk-through continues in Listing 2-6.
    // on the ATmega168, timer 0 is also used for fast
    // hardware PWM (using phase-correct PWM would mean
    // that timer 0 overflowed half as often resulting in
    // different millis() behavior on the ATmega8 and
    // ATmega168)
#if defined(TCCR0A) && defined(WGM01)
    sbi(TCCR0A, WGM01);                 ①
    sbi(TCCR0A, WGM00);
#endif
    // set timer 0 prescale factor to 64
    ...
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
    // this combination is for the standard
    // 168/328/1280/2560.
    sbi(TCCR0B, CS01);                  ②
    sbi(TCCR0B, CS00);
    ...
#else
    #error Timer 0 prescale factor 64 not set correctly
#endif
    // enable timer 0 overflow interrupt
    ...
#elif defined(TIMSK0) && defined(TOIE0)
    sbi(TIMSK0, TOIE0);                 ③
#else
    #error Timer 0 overflow interrupt not set correctly
#endif
Listing 2-6

Timer/counter 0 configuration

  •     ①    Setting these 2 bits in the TCCR0A register ensures that the PWM waveform generator is running in Fast Hardware PWM mode, instead of Phase Correct PWM mode, which would interfere with the timer for the millis() function.

  •     ②    Setting these 2 bits in register TCCR0B sets the timer clock to be the system clock divided by 64.

  • That equates to 16 MHz for the system clock, divided down to 250 MHz, or one tick of the timer clock for every 64 ticks of the system clock.

  •     ③    Setting this bit in the TIMSK0 register enables the Timer 0 Overflow interrupt. Now every time the timer goes from 255 to 0, the interrupt routine will be called to accumulate counts for millis() and micros().

Listing 2-7 shows the source for the Timer 0 Overflow interrupt routine, which is separate from the code in the init() function. The remainder of the init() function walk-through follows later.

2.9.3 Timer/counter 0 Overflow Interrupt

The Timer 0 Overflow interrupt is used to update the millis() count. It does this every 1.024 milliseconds, and, as this is slightly over 1 millisecond, it accumulates these extra fractions; and when there are enough accumulated, the millis count gets incremented by an extra leap millisecond. This takes place roughly every 42 interrupt handler executions – it’s actually every 41.666 (recurring) executions, but you cannot have a fraction of an execution!

The code to do all this is shown in Listing 2-7, taken from $ARDINC/wiring.c.
#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
    // copy these to local variables so they can be
    // stored in registers (volatile variables must be
    // read from memory on every access)
    unsigned long m = timer0_millis;      ①
    unsigned char f = timer0_fract;
    m += MILLIS_INC;                      ②
    f += FRACT_INC;
    if (f >= FRACT_MAX) {
        f -= FRACT_MAX;
        m += 1;
    }
    timer0_fract = f;                    ③
    timer0_millis = m;
    timer0_overflow_count++;             ④
}
Listing 2-7

Timer/counter 0 Overflow interrupt handler

  •     ①    The current values of the variables timer0_millis and timer0_fract are copied locally from memory (Static RAM) so that they can be used in registers for faster processing.

  •     ②    The current timer0_millis count, in m, is incremented by MILLIS_INC. The current accumulated fractions of a millisecond, timer0_fract used locally in variable f, are incremented by FRACT_INC. If f is then larger than FRACT_MAX, then an extra “leap” millisecond is accumulated and the counts adjusted accordingly.

  •     ③    The new values are copied back to the original two variables.

  •     ④    A counter, timer0_overflow_count, keeps track of the number of times the ISR has been fired. This counter is used in the millis() function, and that is itself used in the delay() function.

“What are MILLIS_INC, FRACT_INC, and FRACT_MAX?”, I hear you ask.

These are defined in $ARDINC/wiring.c, and an extract is shown in Listing 2-8.
// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
// the fractional number of milliseconds per timer0 overflow.
// we shift right by three to fit these numbers into a byte.
// For the clock speeds we care about - 8 and 16 MHz - this
// doesn't lose precision.)
#define FRACT_INC
    ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)
Listing 2-8

Variables used in counting millis

The other helper definitions we need here are as follows. The first is also defined in $ARDINC/wiring.c:
// the prescaler is set so that timer0 ticks every 64
// clock cycles, and the overflow handler is called
// every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW
    (clockCyclesToMicroseconds(64 * 256))
From $ARDINC/Arduino.h, we have
#define clockCyclesToMicroseconds(a)
    ( (a) / clockCyclesPerMicrosecond() )
and also
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )

So, working backward, we see that the number of clockCyclesPerMicrosecond is 16e6/1e6 or 16. From that, we can then see that MICROSECONDS_PER_TIMER0_OVERFLOW is (64 * 256)/16, which gives us 1024.

This then allows MILLIS_INC to be calculated as 1024/1000 which is a solitary one, as this is integer division, not floating point. And, finally, FRACT_INC is 1024 – (1024/16) >> 3 or 24/8 which gives us 3.

FRACT_MAX is easy; it’s effectively 1000/8 or 125.

So, every 256 Timer/counter 0 clock ticks, we increment the number of millis by 1 and add 3 to the fractions accumulator, and if that is more than 125, we add an extra 1 to millis and reduce the fractions accumulator by 125, thus holding on to any spare fractions. Eventually, these will add up and generate another millisecond.

If you are wondering why we add 3 and check for 125, then consider that there are 24 microseconds spare each time through the interrupt handler – that’s 41.666 (recurring) to gain an extra millisecond. 125/3 is exactly the same value – 41.666 (recurring) – so it works out the same.

../images/494109_1_En_2_Chapter/494109_1_En_2_Figx_HTML.gif Is adding 3 and checking against 125 more efficient than adding 24 and checking against 1000? Yes, indeed, the former method fits a byte, while the latter requires a 16-bit value, and the ATmega328P is an 8-bit device without a 16-bit compare instruction.

2.9.4 Configuring Timer/counter 1 and Timer/counter 2

On the ATmega328P, Timer/counter 1 is a 16-bit timer; however, the Arduino system sets it up so that it appears as an 8-bit timer which makes it similar to Timer/counter 0 and Timer/counter 2.

Timer/counters 1 and 2 are used to provide PWM on four of the six pins that are PWM enabled on an ATmega328P.

Both timers have their prescaler set to divide the system clock by 64 and are set up in 8-bit Phase Correct PWM mode.

Timer/counter 1 provides PWM on pins D9 and D10, while Timer/counter 2 provides PWM on pins D3 and D11.

The init() function source code continues in Listing 2-9, where it configures Timer/counter 1.
    // timers 1 and 2 are used for phase-correct
    // hardware PWM. this is better for motors as it
    // ensures an even waveform
    // note, however, that fast PWM mode can achieve a
    // frequency of up 8 MHz (with a 16 MHz clock) at
    // 50% duty cycle
#if defined(TCCR1B) && defined(CS11) && defined(CS10)
    TCCR1B = 0;                          ①
    // set timer 1 prescale factor to 64
    sbi(TCCR1B, CS11);                   ②
#if F_CPU >= 8000000L
    sbi(TCCR1B, CS10);                   ③
#endif
    ...
#endif
    // put timer 1 in 8-bit phase correct PWM mode
#if defined(TCCR1A) && defined(WGM10)
    sbi(TCCR1A, WGM10);                  ④
#endif
Listing 2-9

Timer/counter 1 configuration

  •     ①    This shouldn’t be necessary as init() is called at the start of a sketch, after a reset, or on power on, so the default for register TCCR1B is zero anyway. However, sometimes it’s best to be explicit.

  •     ②    Setting only the CS11 bit sets the timer’s prescaler to divide by 8, which is fine for slow system clock speeds. This would give a standard Arduino board a 2 MHz timer clock speed – a tad excessive perhaps!

  •     ③    For faster clock speeds, setting CS10, plus the preceding CS11, finally sets the prescaler to divide by 64, giving the required 250 KHz timer clock speed.

  •     ④    The WGM10 bit, in the TCCR1A register, sets the PWM waveform generator to run in 8-bit Phase Correct PWM mode.

After configuring Timer/counter 1, the next part of the init() function sets up Timer/counter 2 as shown in Listing 2-10.
    // set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
    ...
#elif defined(TCCR2B) && defined(CS22)
    sbi(TCCR2B, CS22); ①
//#else
    // Timer 2 not finished (may not be present on this CPU)
#endif
    // configure timer 2 for phase correct PWM (8-bit)
#if defined(TCCR2) && defined(WGM20)
    ...
#elif defined(TCCR2A) && defined(WGM20)
    sbi(TCCR2A, WGM20); ②
//#else
    // Timer 2 not finished (may not be present on this CPU)
#endif
    ...
Listing 2-10

Timer/counter 2 configuration

  •     ①    Setting bit CS22 in register TCCR2B sets the timer’s prescaler to divide the system clock by 64. This results in a 250 KHz timer clock.

  •     ②    Setting the WGM20 bit, in the TCCR2A register, sets the PWM waveform generator to run in 8-bit Phase Correct PWM mode.

The function continues, now that all three timers are configured, to set up the Analogue to Digital Converter (ADC) so that the Arduino analogRead() function will work.

2.9.5 Initializing the Analogue to Digital Converter

According to the data sheet for the ATmega328P, the ADC (Analogue to Digital Converter) runs best, and most accurately, when it is running at a speed between 50 and 200 KHz. The system clock on the microcontroller is running at 16 MHz, so is a little on the speedy side.

In order to get the ADC into a valid speed range, it has its prescaler set to divide the system clock by 128. This puts the speed at 125 KHz, which is within the desired range specified by the data sheet.

The ADC is then enabled, as shown in Listing 2-11, which is a continuation of the init() function.
#if defined(ADCSRA)
    // set a2d prescaler so we are inside the
    // desired 50-200 KHz range.
    #if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
        sbi(ADCSRA, ADPS2);        ①
        sbi(ADCSRA, ADPS1);
        sbi(ADCSRA, ADPS0);
        // Code removed - not relevant.
    #endif
    // enable a2d conversions
    sbi(ADCSRA, ADEN);             ②
#endif
Listing 2-11

ADC configuration

  •     ①    The system clock needs to be divided down to obtain an ADC clock speed in the range 50–200 KHz which, according to the data sheet, is the optimal clock range for the ADC. For the standard Arduino boards, this requires a divisor of 128 to get the 16 MHz system clock into this range. The resulting ADC clock speed is 125 KHz, which is well within the requirement.

  •     ②    Setting the ADEN bit in the ADCSRA register ensures that the ADC is enabled. It will not start converting until it is told to do so by analogRead().

The preceding code implies that even if you don’t want the ADC in your sketches, it is active and consuming additional power that might be better used keeping your batteries from running down!

If you are sure that you don’t need or want analogRead() in your sketch, and you are running on batteries, then perhaps adding the following line to your setup() function could help:
#include <wiring_private.h>
...
cbi(ADCSRA, ADEN);
This disables the ADC. If you also add
sbi(PRR, PRADC);
then you will also stop power reaching the ADC clock, saving a few more microAmps, alternatively:
#include <avr/power.h>
...
void setup()
{
    ...
    power_adc_disable();
}

This uses the AVRLib facility to turn off the power to the ADC clock and, in my opinion, is a lot more readable, and understandable, than the preceding one.

2.9.6 Disabling the USART

The final task for the init() function is to disable the Universal Synchronous/Asynchronous Receiver/Transmitter or USART for short.

This is left attached to Arduino pins D0 and D1 by the bootloader, and the two pins used need to be disconnected so that they can be reused for digitalRead() and/or digitalWrite() in sketches. On the ATmega328P, these digital pins are the physical pins 2 and 3.

If the USART is required for the Serial Monitor tool, for example, then the two USART pins will be reconnected by a call to Serial.begin() in the sketch. Listing 2-12 shows the pins being disconnected from the USART.
    // the bootloader connects pins 0 and 1 to the
    // USART; disconnect them here so they can be used
    // as normal digital i/o; they will be reconnected
    // in Serial.begin()
#if defined(UCSRB)
    ...
#elif defined(UCSR0B)
    UCSR0B = 0;
#endif
}      // End of init().
Listing 2-12

USART configuration

This concludes the initialization that occurs at the start of every sketch and the walk-through of the init() function’s source code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.30.232