Arduino programming language. Arduino - Programming Basics
After familiarizing yourself with the basic elements of Arduino, as well as writing a “Hello World!” program. It's time to get acquainted with a programming language.
The structure of the language is based primarily on C/C++, so those who have previously programmed in this language will have no difficulty mastering Arduino programming. Others should learn basic information about control commands, data types, and functions.
Much of the information contained here will be compatible with any C/C++ course, taking into account the differences in data types, as well as a few specific instructions regarding I/O port programming.
Basics
A few formal things, that is, those that everyone knows about, but sometimes forget...
In the Arduino IDE, as in C/C++, you need to be aware of character cases. Keywords such as if, for are always written in lowercase. Each instruction ends with ";". The semicolon tells the compiler which part to interpret as an instruction.
Parentheses (..) are used to denote program blocks. We use them to constrain function bodies (see below), loops, and conditional statements.
It is a good practice to add comments to the program content, this makes the code easy to understand. Single line comments begin with // (double slash). Multiline comments begin with /* and end with */
If we want to include any library in our program, we use the include command. Here are examples of connecting libraries:
#include
Functions in Arduino
A function (subroutine) is a separate part of a program that performs certain operations. Functions are used to simplify the main program and improve code readability. It is useful to use functions as we can easily use them in many of our projects.
A standard programming course contains information about functions that will be presented in the following articles. In the case of Arduino, functions will be discussed at the beginning because even the simplest program must have two special functions. This has already been mentioned in previous articles, but here we will systematize this information.
Function Declaration
The function declaration diagram looks like this:
Type function_name(parameter) ( // instructions for execution (function body) return (/* return value*/); )
type is the name of any available data type in a given programming language. We will provide a list of types available when programming Arduino in a separate article.
After execution, the function will return the value of the declared type. If the function does not accept any return value, then the data type will be “void”.
function_name allows it to be uniquely identified. In order to call (run) a function, we give it a name.
parameter— function call parameter. The parameters are not required, but they are often useful. If we write a function that has no arguments, we leave the parentheses empty.
Inside the brackets “(…)” is the actual body of the function or instruction that we want to execute. We will provide a description of specific instructions in a separate article.
All functions that return a value end with a return statement followed by the return value. Only functions declared with a null pointer ("void") do not contain a return statement. You need to know that the return statement terminates the function regardless of location.
Below are some examples of function declarations.
Void f1() ( //function body) —————————————— int minus() ( //function body return (0); ) ——————————— ——— int plus(int a, int b) ( return (a+b); )
As you can see from the examples, a function declaration can take many forms depending on your needs.
We strongly encourage you to learn and use the functions when writing your own programs. Over time, each programmer accumulates his own library of functions “for all occasions,” which makes it easier and faster to write new programs.
Now that we know how to write our own function, we need to learn how to use it.
Calling a function
We write all functions in one file/program. There is, of course, a more elegant solution, but we will try to describe it next time.
Once we declare a function, we can use it in other functions with the appropriate name and any required parameters. Below are examples of calling the functions we gave above:
F1(); plus(2,2); y=plus(1.5);
As you can see in the examples, a function call is made by specifying its name and the required number of parameters. It is important to always call a function as declared.
If the f1() function is declared without parameters, then no parameters can be specified when calling it, i.e. calling f1(0) will be incorrect.
The function plus(int a, int b) requires exactly two parameters, so calling with one or three parameters is not possible.
Calling y=plus(1,5) will execute the function "plus" with parameters "1" and "5" and store the return value in the variable "y".
setup() and loop() functions.
With knowledge of function declaration and calling, we can move on to the Arduino system functions: setup() And loop(). The Arduino IDE is required to declare these two functions.
setup() is a function that is called automatically when the power is turned on or the RESET button is pressed.
As its name suggests, it is used to set the initial values of variables, declarations of system inputs and outputs, which are usually specified in the initial parameters. Due to its specificity, this function does not return a value and is not called with parameters. The correct setup() function declaration is below:
Void setup () ( // function body - system initialization )
loop() is a function that is called in an infinite loop. This function also does not return a value and is not called with parameters. The correct loop() function declaration is shown below:
Void loop () ( // function body - program code )
As you can see, the loop() function declaration is identical to the setup() function declaration. The difference lies in the performance of these functions by the microcontroller.
We will now analyze the following pseudocode:
Void setup () ( on_led1 (); //turn on led1 off_led1 (); //turn off led1) void loop () ( on_led2 (); //turn on led2 off_led2 (); //turn off led2)
There are two instructions in the setup() function: the first turns on led1 connected to the board (e.g. pin 13) and the second turns off led1.
The loop() function has identical instructions to turn on and off the LED2 connected to the board (e.g. pin 12).
As a result of running the program, led1 will blink once, while led2 will light up and go out as long as the Arduino is powered on.
Pressing the RESET button will cause led1 to flash once again and led2 to flash continuously again.
Let's summarize:
- The setup() and loop() functions are system functions that must be defined in every project. Even in a situation where we don't write any code in one of them, we still have to declare these two functions;
- The setup() function is executed once, loop() is executed continuously;
- We create our own functions in one file;
- We can call our functions both from setup() and loop(), and from other functions;
- Our own functions can be called with parameters and return a value;
- A function call must be made in accordance with its declaration.
In the life of an Arduino developer, sooner or later there comes a time when the standard development environment becomes crowded. If the sketches no longer have enough memory, you need hard realtime and work with interruptions, or you just want to be closer to the hardware, then it’s time to switch to C. Experienced electronics engineers will frown contemptuously at the mention of Arduino and send the newbie to a radio shop for a soldering iron. This may not be the worst advice, but we won't follow it just yet. If we discard the Arduino IDE and the wiring/processing language, we are left with an excellent debugging board, already equipped with everything necessary for the operation of the microcontroller. And, importantly, a bootloader is already hardwired into the controller’s memory, allowing you to load the firmware without using a programmer.
To program in C, we need AVR GCC Toolchain.
We will also need the Arduino IDE installed, because... it contains the avrdude utility, which is needed to download the firmware to the controller. CrossPack also contains avrdude, but the version that comes with it does not work with Arduino.
After everything is installed, let's create our first project. To begin with, let's write Makefile. It will allow us to avoid entering long commands manually every time we compile and load the firmware.
#Controller installed on the board. May be different, for example atmega328 DEVICE = atmega168 #Clock frequency 16 MHz CLOCK = 16000000 #Avrdude launch command. It needs to be copied from the Arduino IDE. AVRDUDE = /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avrdude -C/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf -carduino -P/dev/tty.usbserial-A600dAAQ -b19200 -D -p atmega168 OBJECTS = main.o COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) all: main .hex .c.o: $(COMPILE) -c $< -o $@ .S.o: $(COMPILE) -x assembler-with-cpp -c $< -o $@ .c.s: $(COMPILE) -S $< -o $@ flash: all $(AVRDUDE) -U flash:w:main.hex:i clean: rm -f main.hex main.elf $(OBJECTS) main.elf: $(OBJECTS) $(COMPILE) -o main.elf $(OBJECTS) main.hex: main.elf rm -f main.hex avr-objcopy -j .text -j .data -O ihex main.elf main.hex avr-size --format=avr --mcu=$(DEVICE) main.elf
In this file we need to enter our command to launch avrdude. It will look different on different systems. To find out your option, launch the Arduino IDE and check the “Show verbose output during upload” box in the settings.
Now we load any sketch into Arduino and look at the messages displayed at the bottom of the window. We find the avrdude call there, copy everything except the -Uflash parameter and paste it into the Makefile after “AVRDUDE = “.
A quick note: all indentation in the Makefile is done with tab characters (Tab key). If your text editor replaces these characters with spaces, the make command will refuse to build the project.
Now let's create a file main.c- the actual text of our program, in which we traditionally blink the LED.
#include
Our project is ready. Let’s open the console in our project directory and enter the “make” command:
As you can see, the size of the resulting firmware is only 180 bytes. A similar Arduino sketch takes up 1116 bytes in the controller memory.
Now let's go back to the console and enter "make flash" to load the compiled file into the controller:
If the download was completed without errors, the LED connected to pin 13 of the board will blink happily. Sometimes avrdude cannot find the board or falls off due to a timeout - in this case, twisting the USB cable can help. Also, to avoid board access conflicts, do not forget to close the Arduino IDE before the “make flash” command.
Perhaps many of the things described in this article will seem obvious to seasoned developers. I tried to describe the process in a language that is as understandable as possible for a novice Arduino user and to collect in one place the information that I was able to obtain from various sources and verified empirically. Maybe this article will save someone a couple of hours of time.
Good luck in mastering microcontrollers!
So you have a processor. You probably understand that the processor can be somehow programmed to do what you want. In order for useful work to be done, it is necessary to (a) write a useful program and (b) give it to the processor for execution.
In general, it doesn’t matter what kind of processor you have: the latest Intel Pentium in your laptop or a microcontroller on an Arduino board. Principles of writing a program, i.e. programming, in both cases the same. The only difference is the speed and scope of capabilities for working with other devices.
What is a program and where to write it
The processor, despite all the complexity of production, is essentially a rather simple and straightforward thing. He doesn't know how to think. He can only blindly, byte by byte, execute the instructions that were handed to him. A rough example of the sequence of instructions can be given:
Instruction byte | What does it mean for the processor |
---|---|
00001001 | means: take the next byte and store it in cell No. 1 |
00000110 | ...this is exactly the next byte that we remember in cell No. 1: the number 5 |
00011001 | means: subtract one from the value in cell No. 1 and leave the updated result there |
00101001 | means: compare the value in cell No. 1 with zero and if it is zero, jump through as many bytes as indicated in the next byte |
00000100 | ...if the result was zero, we want to jump 4 bytes to the penultimate instruction |
10000011 | |
01000001 | ...the letter “A” corresponds exactly to this code |
00101000 | means that we want to jump back as many bytes as indicated in the next byte |
00000110 | ...we will jump 6 bytes back to instruction No. 3 |
10000011 | means that we want to display the character whose code is written in the next byte |
00100001 | ...sign "!" This code exactly matches |
As a result of executing this sequence of instructions, the panic phrase “AHHH!” will be displayed on the screen.
Quite a lot of code for such a simple purpose! It is clear that if all programs were written like this, directly, the development of complex products would take centuries.
Why are programming languages needed?
To simplify the task a million times, programming languages were invented. There are a lot of them, and even from those that are constantly heard, you can quickly remember a dozen or two: Assembler, C, C++, C#, Java, Python, Ruby, PHP, Scala, JavaScript.
Programs in these languages are much closer to natural human language. And therefore they are easier, faster and more pleasant to write, and most importantly, they are much simpler read: to you immediately after writing, to you a year later, or to your colleague.
The problem is that such languages are not understandable to the processor and before you give it this program, you need to compile: translate from natural language into those same instructions in the form of zeros and ones. This is done by programs called compilers. Each language, unless it remains at the fantasy level, has its own compiler. For popular languages, there are usually several to choose from, from different manufacturers and for different platforms. Most of them are freely available on the Internet.
So, there are programs in a language that is completely understandable to humans: they are also called “source code”, simply “code” or “source codes”. They are written to simple text files using any text editor, even using notepad. Then they are turned into sets of zeros and ones understandable to the processor using the compiler: the compiler receives the source code as input, and creates binary executable, the one understandable to the processor.
Binary files are not readable and are generally intended only for execution by the processor. They can be of different types depending on what they were received for: .exe are programs for Windows, .hex are programs for execution by a microcontroller such as Arduino, etc.
Why are there so many programming languages and what is the difference?
Why? Because there are many people and companies on Earth, and many believed that they could do the best: more convenient, clearer, faster, slimmer.
What is the difference: different languages have a different balance of writing speed, readability and execution speed.
Let's look at the same program that displays a song about 99 bottles of beer on the screen in different programming languages.
For example, the Perl language. It is written quickly; it is impossible to understand what the programmer meant; executed slowly:
sub b( $n = 99 - @_ - $_ || No; "$n bottle" . "s" x!!-- $n . "of beer" ) ; $w = "on the wall" ; die map ( b. "$w, \n". b. ", \n Take one down, pass it around,\n ". b(0) . "$w. \n\n"} 0 .. 98Java language. It takes a relatively long time to write; easy to read; executes quite quickly, but takes up a lot of memory:
class bottles ( public static void main( String args) ( String s = "s" ; for (int beers= 99 ; beers>- 1 ; ) ( System.out .print (beers + "bottle" + s + "of beer on the wall, " ) ; System.out .println (beers + "bottle" + s + "of beer, " ) ; if (beers== 0 ) ( System.out .print ( "Go to the store, buy some more,") ; System.out .println ( "99 bottles of beer on the wall.\n ") ; System.exit(0); ) else System.out .print ( "Take one down, pass it around,") ; s = (-- beers == 1) ? "" : "s" ; System.out .println (beers + "bottle" + s + " of beer on the wall.\n ") ; } } }Assembly language. It takes a long time to write; difficult to read; executes very quickly:
code segment assume cs : code , ds : code org 100h start : ; Main loop mov cx, 99; bottles to start with loopstart: call printcx ; print the number mov dx , offset line1 ; print the rest of the first line mov ah, 9; MS-DOS print string routine int 21h call printcx ; print the number mov dx , offset line2_3 ; rest of the 2nd and 3rd lines mov ah, 9 int 21h dec cx; take one down call printcx ; print the number mov dx , offset line4 ; print the rest of the fourth line mov ah, 9 int 21h cmp cx, 0; Out of beer? jne loopstart ; if not, continue int 20h ; quit to MS-DOS ; subroutine to print CX register in decimal printcx: mov di , offset numbufferend ; fill the buffer in from the end mov ax, cx ; put the number in AX so we can divide it printcxloop: mov dx, 0 ; high-order word of numerator - always 0 mov bx, 10 div bx ; divide DX:AX by 10. AX=quotient, DX=remainder add dl , "0" ; convert remainder to an ASCII character mov[ds:di],dl; put it in the print buffer cmp ax, 0; Any more digits to compute? je printcxend; if not, end dec di ; put the next digit before the current one jmp printcxloop ; loop printcxend: mov dx , di ; print, starting at the last digit computed mov ah, 9 int 21h ret; Data line1 db "bottles of beer on the wall,", 13 , 10 , "$" line2_3 db "bottles of beer," 13 , 10 , "Take one down, pass it around,", 13 , 10 , "$" line4 db "bottles of beer on the wall.", 13 , 10 , 13 , 10 , "$" numbuffer db 0 , 0 , 0 , 0 , 0 numbufferend db 0 , "$" code ends end startHow is Arduino programmed?
If we talk about Arduino or microcontrollers from Atmel, in what language can you write programs for them? The theoretical answer: any. But in practice, the choice is limited to the languages Assembler, C and C++. This is due to the fact that compared to a desktop computer, they have very limited resources. Kilobytes of memory, not gigabytes. Megahertz on the processor, not gigahertz. This is a price to pay for cheapness and energy efficiency.
Therefore, we need a language that can compile and execute efficiently. That is, it is translated into those very zeros and ones from instructions as optimally as possible, without wasting precious instructions and memory. The aforementioned languages have this kind of efficiency. Using them even within the narrow limits of microcontroller resources, you can write feature-rich programs that run quickly.
Assembler, as you have seen, cannot be called the simplest and most elegant and, as a result, the flagship language for Arduino is C/C++.
Many sources say that Arduino is programmed in the Arduino, Processing, Wiring language. This is not an entirely correct statement. Arduino is programmed in C/C++, and what is called in these words is just a convenient “body kit” that allows you to solve many common problems without reinventing the wheel every time.
Why are C and C++ mentioned in the same sentence? C++ is a superstructure of C. Every C program is a valid C++ program, but not vice versa. You can use both. More often than not, you won't even think about what you're using when solving the current problem.
Closer to the point: the first program
Let's write the first program for Arduino and make the board execute it. You need to create a text file with the source code, compile it and feed the resulting binary file to the microcontroller on the board.
Let's go in order. Let's write the source code. You can write it in notepad or any other editor. However, to make the work convenient, there are so-called development environments (IDE: Integrated Development Environment). In the form of a single tool, they provide a text editor with highlighting and hints, a compiler launched by a button, and many other joys. For Arduino, this environment is called Arduino IDE. It is freely available for download on the official website.
Install the environment and run it. In the window that appears, you will see: most of the space is given to the text editor. The code is written into it. Code in the Arduino world is also called sketch.
So let's write a sketch that doesn't do anything. That is, the minimum possible correct program in C++, which simply wastes time.
void setup() ( ) void loop() ( )Let's not focus on the meaning of the written code for now. Let's compile it. To do this, in the Arduino IDE, there is a “Verify” button on the toolbar. Click it and in a few seconds the binary file will be ready. This will be announced by the inscription “Done compiling” under the text editor.
As a result, we have a binary file with the extension .hex, which can be executed by the microcontroller.
Now you need to slip it into the Arduino. This process is called booting, flashing or flooding. To upload to the Arduino IDE, there is an “Upload” button on the toolbar. Connect the Arduino to your computer via a USB cable, click “Upload” and in a few moments the program will be uploaded to the Arduino. In this case, the program that was there previously will be erased.
The successful firmware will be announced by the inscription “Done Uploading”.
If you encounter an error when trying to download, make sure that:
In the Tools → Board menu, select the port to which the Arduino is actually connected. You can plug the USB cable in and out to see which port appears and disappears: this is the Arduino.
You have installed the necessary drivers for Arduino. This is required on Windows, not required on Linux, and only required on older boards prior to Arduino Duemilanove on MacOS.
Congratulations! You've gone all the way from a blank slate to a working program in Arduino. She may not do anything, but this is already a success.
Does studying microcontrollers seem complicated and incomprehensible? Before Arudino appeared, it was really not easy and required a certain set of programmers and other equipment.
This is a kind of electronic designer. The initial goal of the project is to allow people to easily learn how to program electronic devices, while devoting minimal time to the electronic part.
The assembly of complex circuits and the connection of boards can be carried out without a soldering iron, but with the help of jumpers with detachable male and female connections. This way, both mounted elements and expansion boards can be connected, which in the Arduino lexicon are simply called “Shields”.
What is the first Arduino board to buy for a beginner?
It is considered the basic and most popular board. This card is the size of a credit card. Quite large. Most shields that are on sale fit perfectly with it. The board has sockets for connecting external devices.
In domestic stores in 2017 its price is about 4-5 dollars. On modern models, its heart is Atmega328.
Image of the Arduino board and explanation of the functions of each pin, Arduino UNO pinout
The microcontroller on this board is a long chip in a DIP28 package, which means that it has 28 legs.
The next most popular board costs almost twice as much as the previous one - 2-3 dollars. This is a fee. The current boards are built on the same Atmega328, they are functionally similar to UNO, the differences are in size and solution for coordinating with USB, more on this later. Another difference is that pin-shaped plugs are provided for connecting devices to the board.
The number of pins (legs) of this board is the same, but you can see that the microcontroller is made in a more compact TQFP32 package, ADC6 and ADC7 are added to the case, the other two “extra” legs duplicate the power bus. Its size is quite compact - about the size of your thumb.
The third most popular board is, it does not have a USB port for connecting to a computer; I will tell you how the connection is made a little later.
This is the smallest board of all reviewed, otherwise it is similar to the previous two, and its heart is still the Atmega328. We will not consider other boards, since this is an article for beginners, and comparing boards is the topic of a separate article.
At the top there is a USB-UART connection diagram, the “GRN” pin is connected to the reset circuit of the microcontroller, it can be called differently, you will find out why this is needed later.
While UNO is great for prototyping, Nano and Pro Mini are great for finishing your project because they take up little space.
How to connect Arduino to computer?
Arduino Uno and Nano connect to the computer via USB. However, there is no hardware support for the USB port; a level conversion circuit solution, usually called USB-to-Serial or USB-UART (rs-232), is used here. At the same time, a special Arduino bootloader is flashed into the microcontroller, which allows flashing via these buses.
The Arduino Uno implements this connection on a microcontroller with USB support - ATmega16U2 (AT16U2). It turns out that an additional microcontroller on the board is needed to flash the firmware of the main microcontroller.
In Arduino Nano this is implemented by the FT232R chip, or its analogue CH340. This is not a microcontroller - it is a level converter, this fact makes it easier to assemble the Arduino Nano from scratch with your own hands.
Typically, drivers are installed automatically when you connect the Arduino board. However, when I bought a Chinese copy of the Arduino Nano, the device was identified, but it did not work, there was a round sticker on the converter with information about the release date, I don’t know if this was done on purpose, but when I peeled it off I saw the CH340 marking.
I had never encountered this before and thought that all USB-UART converters were assembled on FT232, I had to download the drivers, they are very easy to find by searching for “Arduino ch340 drivers”. After a simple installation, everything worked!
The microcontroller can also be powered through the same USB port, i.e. if you connect it to an adapter from a mobile phone, your system will work.
What should I do if my board does not have USB?
The Arduino Pro Mini board has smaller dimensions. This was achieved by removing the USB connector for firmware and the same USB-UART converter. Therefore, it must be purchased separately. The simplest converter based on CH340 (the cheapest), CPL2102 and FT232R, costs from 1 dollar.
When purchasing, pay attention to what voltage this adapter is designed for. Pro mini comes in 3.3 and 5 V versions; the converters often have a jumper for switching the supply voltage.
When flashing the Pro Mini firmware, immediately before starting it you need to press RESET, however, in converters with DTR this is not necessary, the connection diagram is in the figure below.
They are connected with special “Mama-Mama” (female-female) terminals.
Actually, all connections can be made using such terminals (Dupont), they come either with sockets on both sides, or with plugs, or with a socket on one side and a plug on the other.
How to write programs for Arduino?
To work with sketches (the name of the firmware in the language of Arduino engineers), there is a special integrated development environment for Arduino IDE, you can download it for free from the official website or from any thematic resource; there are usually no problems with installation.
This is what the program interface looks like. You can write programs in a simplified C AVR language specially designed for Arduino, essentially a set of libraries called Wiring, as well as in pure C AVR. The use of which simplifies the code and speeds up its operation.
At the top of the window there is a familiar menu where you can open a file, settings, select the board you are working with (Uno, Nano and many, many others) and also open projects with ready-made code examples. Below is a set of buttons for working with the firmware; you will see the assignment of the keys in the figure below.
At the bottom of the window there is an area for displaying information about the project, the status of the code, firmware and the presence of errors.
Arduino IDE Programming Basics
At the beginning of the code you need to declare variables and include additional libraries, if any, this is done as follows:
#include biblioteka.h; // connect the library called “Biblioteka.h”
#define changenaya 1234; // Declare a variable with the value 1234
The Define command allows the compiler to choose the type of the variable itself, but you can set it manually, for example, an integer int, or a floating-point float.
int led = 13; // created the variable “led” and assigned it the value “13”
The program can determine the state of the pin as 1 or 0. 1 is a logical unit, if pin 13 is equal to 1, then the voltage on its physical leg will be equal to the supply voltage of the microcontroller (for Arduino UNO and Nano - 5 V)
A digital signal is written using the digitalWrite (pin, value) command, for example:
digitalWrite(led, high); //write one to pin 13 (we declared it above) log. Units.
As you can understand, ports are accessed according to the numbering on the board, corresponding to the number. Here is an example similar to the previous code:
digitalWrite(13, high); // set pin 13 to one
A frequently used time delay function is called by the delay() command, the value of which is specified in milliseconds, microseconds are achieved using
delayMicroseconds() Delay(1000); //microcontroller will wait 1000 ms (1 second)
The input and output port settings are specified in the void setup() function with the command:
pinMode(NOMERPORTA, OUTPUT/INPUT); // arguments - variable name or port number, input or output to choose from
Understanding the first Blink program
As a kind of “Hello, world” for microcontrollers, there is a program for blinking an LED, let’s look at its code:
At the beginning, with the pinMode command, we told the microcontroller to assign the port with the LED to the output. You have already noticed that in the code there is no declaration of the “LED_BUILTIN” variable, the fact is that in Uno, Nano and other boards, a built-in LED is connected to pin 13 from the factory and it is soldered on the board. It can be used by you for display in your projects or for simple testing of your flashing programs.
Next, we set the pin to which the LED is soldered to one (5 V), the next line makes the MK wait 1 second, and then sets the LED_BUILTIN pin to zero, waits a second and the program repeats in a circle, so when LED_BUILTIN is equal to 1 - LED( and any other load connected to the port) is turned on, when at 0 it is turned off.
We read the value from the analog port and use the read data
The AVR Atmega328 microcontroller has a built-in 10-bit analog-to-digital converter. The 10-bit ADC allows you to read voltage values from 0 to 5 volts, in steps of 1/1024 of the entire signal amplitude swing (5 V).
To make it clearer, let’s consider the situation, let’s say the voltage value at the analog input is 2.5 V, which means the microcontroller will read the value from pin “512”, if the voltage is 0 - “0”, and if 5 V - (1023). 1023 - because counting starts from 0, i.e. 0, 1, 2, 3, etc. up to 1023 - 1024 values in total.
This is what it looks like in code, using the standard “analogInput” sketch as an example
int sensorPin = A0;
int ledPin = 13;
int sensorValue = 0;
pinMode(ledPin, OUTPUT);
sensorValue = analogRead(sensorPin);
digitalWrite(ledPin, HIGH);
delay(sensorValue);
digitalWrite(ledPin, LOW);
delay(sensorValue);
We declare variables:
Ledpin - we independently assign a pin with a built-in LED to the output and give it an individual name;
sensorPin - analog input, set according to the markings on the board: A0, A1, A2, etc.;
sensorValue - a variable for storing the integer read value and further working with it.
The code works like this: sensorValue saves the analog value read from sensorPin (analogRead command). - here the work with the analog signal ends, then everything is as in the previous example.
We write one to ledPin, the LED turns on and wait for a time equal to the sensorValue value, i.e. from 0 to 1023 milliseconds. We turn off the LED and wait for this period of time again, after which the code repeats.
Thus, by positioning the potentiometer we set the blinking frequency of the LED.
Map function for Arudino
Not all functions for actuators (I don’t know of any) support “1023” as an argument, for example, the servo is limited by the rotation angle, i.e. per half turn (180 degrees) (half turn) of the servo motor the maximum function argument is “180”
Now about the syntax: map (the value we are translating, the minimum input value, the maximum input value, the minimum output value, the maximum output value).
In code it looks like this:
(map(analogRead(pot), 0, 1023, 0, 180));
We read the value from the potentiometer (analogRead(pot)) from 0 to 1023, and at the output we get numbers from 0 to 180
Value map values:
In practice, we apply this to the operation of the code of the same servo drive, take a look at the code from the Arduino IDE, if you carefully read the previous sections, then it does not require any explanation.
And the connection diagram.
Conclusions Arduino is a very convenient tool for learning to work with microcontrollers. And if you use pure C AVR, or as it is sometimes called “Pure C”, you will significantly reduce the weight of the code, and more of it will fit in the microcontroller’s memory, as a result you will get an excellent factory-made debugging board with the ability to flash firmware via USB.
I like Arduino. It’s a pity that many experienced microcontroller programmers groundlessly criticize it for being too simplified. In principle, only the language is simplified, but no one forces you to use it, plus you can flash the microcontroller via the ICSP connector and upload the code you want there, without any unnecessary bootloaders.
For those who want to play around with electronics, like an advanced designer, this is perfect, and for experienced programmers, as a board that does not require assembly, it will also be useful!
For more information about Arduino and the features of its use in various circuits, see the e-book - .
Introduction
Freeduino/Arduino is programmed in a special programming language - it is based on C/C++, and allows you to use any of its functions. Strictly speaking, there is no separate Arduino language, just as there is no Arduino compiler - written programs are converted (with minimal changes) into a program in C/C++, and then compiled by the AVR-GCC compiler. So, in fact, a variant of C/C++ specialized for AVR microcontrollers is used.
The difference is that you get a simple development environment and a set of basic libraries that simplify access to the peripherals located “on board” the microcontroller.
Agree, it is very convenient to start working with a serial port at a speed of 9600 bits per second, making a call in one line:
Serial.begin(9600);
And when using “naked” C/C++, you would have to deal with the documentation for the microcontroller and call something like this:
UBRR0H = ((F_CPU / 16 + 9600 / 2) / 9600 - 1) >> 8;
UBRR0L = ((F_CPU / 16 + 9600 / 2) / 9600 - 1);
sbi(UCSR0B, RXEN0);
sbi(UCSR0B, TXEN0);
sbi(UCSR0B, RXCIE0);
Here is a brief overview of the main functions and features of Arduino programming. If you are not familiar with the syntax of the C/C++ languages, we recommend that you refer to any literature on this issue or Internet sources.
On the other hand, all the examples presented are very simple, and most likely you will not have any difficulties understanding the source texts and writing your own programs even without reading additional literature.
More complete documentation (in English) is presented on the official website of the project - http://www.arduino.cc. There is also a forum, links to additional libraries and their descriptions.
By analogy with the description on the official website of the Arduino project, a “port” refers to a microcontroller contact connected to a connector under the corresponding number. In addition, there is a serial communication port (COM port).
Program structure
In your program you must declare two main functions: setup() and loop().
The setup() function is called once, after every power-up or reset of the Freeduino board. Use it to initialize variables, set operating modes of digital ports, etc.
The loop() function sequentially executes the commands described in its body over and over again. Those. After the function completes, it will be called again.
Let's look at a simple example:
void setup() // initial settings
{
beginSerial(9600); // setting the serial port speed to 9600 bps
pinMode(3, INPUT); // setting the 3rd port for data input
}
// The program checks the 3rd port for the presence of a signal on it and sends a response to
// as a text message to the computer's serial port
void loop() // program body
{
if (digitalRead(3) == HIGH) // condition for polling the 3rd port
serialWrite("H"); // send a message in the form of the letter "H" to the COM port
else
serialWrite("L"); // send a message in the form of the letter "L" to the COM port
delay(1000); // delay 1 sec.
}
pinMode(port, mode);
Description:
Configures the specified port to input or output a signal.
Parameters:
port – the number of the port whose mode you want to set (an integer value from 0 to 13).
mode - either INPUT (input) or OUTPUT (output).
pinMode(13, OUTPUT); //13th pin will be the output
pinMode(12, INPUT); //and the 12th is the input
Note:
Analog inputs can be used as digital inputs/outputs by accessing them using numbers 14 (analog input 0) to 19 (analog input 5)
digitalWrite(port, value);
Description:
Sets the voltage level to high (HIGH) or low (LOW) on the specified port.
Parameters:
port: port number
value: HIGH or LOW
digitalWrite(13, HIGH); // set pin 13 to “high” state
value = digitalRead(port);
Description:
Reads the value on the specified port
Parameters:
port: polled port number
Return value: returns the current value on the port (HIGH or LOW) of type int
int val;
val = digitalRead(12); // poll the 12th pin
Note:
If there is nothing connected to the port being read, then the digitalRead() function may return HIGH or LOW values erratically.
Analog signal input/output
value = analogRead(port);
Description:
Reads a value from the specified analog port. Freeduino contains 6 channels, analog-to-digital converter of 10 bits each. This means that an input voltage from 0 to 5V is converted to an integer value from 0 to 1023. The readout resolution is: 5V/1024 values = 0.004883 V/value (4.883 mV). It takes approximately 100 nS (0.0001 C) to read an analog input value, so the maximum read rate is approximately 10,000 times per second.
Parameters:
Return Value: Returns an int number in the range 0 to 1023 read from the specified port.
int val;
val = analogRead(0); // read the value at the 0th analog input
Note:
Analog ports are defined as signal input by default and, unlike digital ports, do not need to be configured by calling the pinMode function.
analogWrite(port, value);
Description:
Outputs an analog value to the port. This function works on: 3, 5, 6, 9, 10, and 11 Freeduino digital ports.
Can be used to change the brightness of an LED, control a motor, etc. After calling the analogWrite function, the corresponding port begins to operate in voltage pulse-width modulation mode until there is another call to the analogWrite function (or digitalRead / digitalWrite functions on the same port).
Parameters:
port: number of the analog input being polled
value: an integer between 0 and 255. A value of 0 generates 0 V on the specified port; a value of 255 generates +5V on the specified port. For values between 0 and 255, the port begins to rapidly alternate between 0 and +5 V voltage levels - the higher the value, the more often the port generates the HIGH (5 V) level.
analogWrite(9, 128); // set pin 9 to a value equivalent to 2.5V
Note:
There is no need to call pinMode to set the port to output signals before calling analogWrite.
The signal generation frequency is approximately 490 Hz.
time = millis();
Description:
Returns the number of milliseconds since the Freeduino executed the current program. The counter will overflow and reset after approximately 9 hours.
Return value: returns an unsigned long value
unsigned long time; // declaration of a time variable of type unsigned long
time = millis(); // transfer the number of milliseconds
delay(time_ms);
Description:
Pauses the program for the specified number of milliseconds.
Parameters:
time_ms – program delay time in milliseconds
delay(1000); //pause 1 second
delayMicroseconds
delayMicroseconds(time_μs);
Description:
Pauses the program for the specified number of microseconds.
Parameters:
time_μs – program delay time in microseconds
delayMicroseconds(500); //pause 500 microseconds
pulseIn(port, value);
Description:
Reads a pulse (high or low) from a digital port and returns the pulse duration in microseconds.
For example, if the value parameter is set to HIGH when calling the function, then pulseIn() waits for a high signal to arrive on the port. From the moment it arrives, the countdown begins until a low signal level is received at the port. The function returns the pulse length (high level) in microseconds. Works with pulses from 10 microseconds to 3 minutes. Note that this function will not return a result until a pulse is detected.
Parameters:
port: port number from which we read the pulse
value: pulse type HIGH or LOW
Return value: returns the pulse duration in microseconds (type int)
int duration; // declaration of a duration variable of type int
duration = pulseIn(pin, HIGH); // measure the pulse duration
Serial data transfer
Freeduino has a built-in controller for serial data transmission, which can be used both for communication between Freeduino/Arduino devices and for communication with a computer. On a computer, the corresponding connection is represented by a USB COM port.
Communication occurs over digital ports 0 and 1, and therefore you will not be able to use them for digital I/O if you are using serial functions.
Serial.begin(baud_rate);
Description:
Sets the COM port information transfer rate in bits per second for serial data transmission. In order to communicate with a computer, use one of these standardized speeds: 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. You can also define other speeds when communicating with another microcontroller by ports 0 and 1.
Parameters:
baud_rate: Data flow rate in bits per second.
Serial.begin(9600); //set the speed to 9600 bps
Serial.available
count = Serial.available();
Description:
Bytes received via the serial port end up in the microcontroller buffer, from where your program can read them. The function returns the number of bytes accumulated in the buffer. The serial buffer can store up to 128 bytes.
Return value:
Returns an int value - the number of bytes available for reading in the serial buffer, or 0 if nothing is available.
if (Serial.available() > 0) ( // If there is data in the buffer
// there should be data reception and processing here
}
char = Serial.read();
Description:
Reads the next byte from the serial port buffer.
Return value:
The first available byte of incoming data from the serial port, or -1 if there is no incoming data.
incomingByte = Serial.read(); // read byte
Description:
Clears the serial port input buffer. Data in the buffer is lost, and further calls to Serial.read() or Serial.available() will make sense for data received after the Serial.flush() call.
Serial.flush(); // Clear the buffer - start receiving data “from scratch”
Description:
Output data to serial port.
Parameters:
The function has several call forms depending on the type and format of the output data.
Serial.print(b, DEC) prints an ASCII string - the decimal representation of the number b.
int b = 79;
Serial.print(b, HEX) prints an ASCII string - the hexadecimal representation of the number b.
int b = 79;
Serial.print(b, OCT) prints an ASCII string - the octal representation of the number b.
int b = 79;
Serial.print(b, OCT); //will output the string “117” to the port
Serial.print(b, BIN) prints an ASCII string - the binary representation of the number b.
int b = 79;
Serial.print(b, BIN); //will output the string “1001111” to the port
Serial.print(b, BYTE) prints the low byte of b.
int b = 79;
Serial.print(b, BYTE); //will output the number 79 (one byte). In the monitor
//from the serial port we get the symbol “O” - its
//code is 79
Serial.print(str) if str is a string or character array, transfers str to the COM port byte byte.
char bytes = (79, 80, 81); //array of 3 bytes with values 79,80,81
Serial.print("Here our bytes:"); //outputs the line “Here our bytes:”
Serial.print(bytes); //outputs 3 characters with codes 79,80,81 –
//these are the characters "OPQ"
Serial.print(b) if b is of type byte or char, prints the number b itself to the port.
char b = 79;
Serial.print(b); //will output the character “O” to the port
Serial.print(b) if b is of type integer, prints the decimal representation of b to the port.
int b = 79;
Serial.print(b); //will output the string “79” to the port
Description:
The Serial.println function is similar to the Serial.print function, and has the same call options. The only difference is that two additional characters are output after the data - a carriage return character (ASCII 13, or "\r") and a newline character (ASCII 10, or "\n").
Example 1 and example 2 will output the same thing to the port:
int b = 79;
Serial.print(b, DEC); //will output the string “79” to the port
Serial.print("\r\n"); //will display the characters "\r\n" – line feed
Serial.print(b, HEX); //will output the string “4F” to the port
Serial.print("\r\n");//will print the characters "\r\n" – line feed
int b = 79;
Serial.println(b, DEC); //will output the string “79\r\n” to the port
Serial.println(b, HEX); //will output the string “4F\r\n” to the port
In the serial port monitor we get.