Categories: Microcontroller circuits
Number of views: 41940
Comments on the article: 5

Methods for reading and managing Arduino I / O ports

 

To interact with the outside world, you need to configure the outputs of the microcontroller to receive or transmit a signal. As a result, each pin will work in the input and output mode. There are two ways to do this on every Arduino board you love, exactly how you learn from this article.

Methods for reading and managing Arduino I / O ports

Method One - The Standard Language for the Arduino IDE

Everyone knows that Arduino It is programmed in C ++ with some adaptation and simplifications for beginners. It is called Wiring. Initially, all arduino ports are defined as inputs, and there is no need to specify this in the code.

Ports are usually written in the variable initialization function:

void setup ()
{
// the code
}

The pinMode command is used for this, it has a fairly simple syntax, first the port number is indicated, then its role is separated by commas.

pinMode (nomer_porta, naznachenie)

With this command, the internal circuitry of the microcontroller is configured in a specific way.

There are three modes in which the port can work: INPUT - input, in this mode occurs reading data from sensors, button status, analog and digital signal. The port is located in the so-called high-impedance state, in simple words - the input has high resistance. This value is set, for example, 13 pin of the board, if necessary, as follows:

pinMode (13, INPUT);

OUTPUT - output, depending on the command prescribed in the code, the port takes a value of one or zero. The output becomes a kind of controlled power source and produces a maximum current (in our case, 20 mA and 40 mA at the peak) connected to the load. To assign a port as an output to Arduino you need to enter:

pinMode (13, OUTPUT);

INPUT_PULLUP - the port works as an input, but the so-called connects to it. 20 kΩ pull-up resistor.

The conditional internal circuitry of the port in this state is shown below. A feature of this input is that the input signal is perceived as inverted (the “unit” at the input is perceived by the microcontroller as “zero”). In this mode, you can not use external pull-up resistors when working with buttons.

pinMode (13, INPUT_PULLUP);

Input pull-up resistor

Data is received from the ports and transmitted to them by the commands:

  • digitalWrite (pin, value) - converts the output pin to logical 1 or 0, respectively, 5V voltage appears or disappears at the output, for example digitalWrite (13, HIGH) - supplies 5 volts (logical unit) to 13 pins, and digitalWrite (13, low ) - translates 13 pins into a state of logical zero (0 volts);

  • digitalRead (pin) - reads the value from the input, example digitalRead (10), reads the signal from 10 pins;

  • analogRead (pin) - reads an analog signal from an analog port, you get a value in the range from 0 to 1023 (within a 10-bit ADC), an example is analogRead (3).


Method two - manage ports through Atmega registers and speed up code

Such control is of course simple, but in this case there are two drawbacks - greater memory consumption and poor performance when working with ports. But remember what is Arduino, regardless of the option board (uno, micro, nano)? First of all, this microcontroller AVR family ATMEGA, recently used MK atmega328.

In the Arduino IDE, you can program in the native language for this family of C AVR, as if you were working with a separate microcontroller. But first things first. To manage Arduino ports this way, you first need to carefully consider the following illustration.

Atmega168 Microcontroller Ports

Perhaps someone will more clearly examine the ports in this form (the same in the figure, but in a different design):

Atmega328 Microcontroller Ports

Here you see the correspondence of the conclusions of Arduino and the names of the ports of Atmega. So, we have 3 ports:

  • PORTB;

  • PORTC;

  • PORTD.

Based on the images shown, I compiled a table of correspondence between the ports of Arduino and Atmega, it will be useful to you in the future.

Concordance table of ports Arduino and Atmega

Atmega has three 8-bit registers that control the state of the ports, for example, port B will figure out their purpose by drawing analogies with the standard wiring tools described at the beginning of this article:

  • PORTB - Manage output status. If the pin is in the "Output" mode, then 1 and 0 determine the presence of the same signals at the output. If the pin is in the “Input” mode, then 1 connects a pull-up resistor (same as the INPUT_PULLUP discussed above), if 0 is a high-impedance state (analogue of INPUT);

  • PINB is a read register. Accordingly, it contains information about the current state of the port pins (logical unit or zero).

  • DDRB - port direction register. With it, you indicate to the microcontroller whether the port is an input or an output, with “1” an output and “0” an input.

Instead of the letter “B”, there may be any other according to the names of the ports, for example, PORTD or PORTC other commands work similarly.

We blink the LED, replace the standard digitalWrite () function. First, let's recall what the original example from the Arduino IDE library looks like.

Arduino LED flashing code

This is the code of the well-known “blink”, which shows the blinking of the LED built into the board.

Pin management

The comments explain the code. The logic of this work is as follows.

The PORTB B00100000 command puts PB5 in the state of a logical unit, look, and those pictures and the table below are located and we see that PB5 corresponds to 13 pin of Arduina.

The letter "B" in front of the numbers indicates that we are writing the values ​​in binary form. Numbering in binary goes from right to left, i.e. here the unit is in the sixth bit from the right edge of the bit, which tells the microcontroller about the interaction with the state of the sixth bit of the port B register (PB5). The table below shows the structure of port D, it is similar and is given as an example.

Port D structure

You can set the value not in binary, but in hexadecimal form, for example, for this we open the windows calculator and in the “VIEW” mode, select the “Programmer” option.

Windows calculator

Enter the desired number:

Programmer Calculator Mode

And click on HEX:

Translation of numbers on a calculator

In this case, we transfer all this to the Arduino IDE, but instead of the prefix "B" it will be "0x".

Number transfer in in Arduino IDE

But with this input there is a problem. If you have anything connected to other pins, then entering a command like B00010000 - you will reset all pins except 13 (PB5). You can enter data for each pin individually. It will look like this:

Entering data into each pin

Such a record may seem incomprehensible, let's figure it out.

Parsing a Record

This is a logical addition operation, | = means adding something to the contents of the port.

Logical addition operation

This means that you need to add a word of 8 bits in the register with a unit shifted by 5 bits - as a result, if 11000010 turns out to be 110,110,010. In this example, it can be seen that only PB5 has changed, the remaining bits of this register have remained unchanged, as well as The state of the microcontroller pins remained unchanged.

But with logical addition, a problem arises - you cannot turn the unit into zero, because:

0+0=1

1+0=1

0+1=1

Logical multiplication and inversion will come to our aid:

Logical Multiplication and Inverting

& = means to multiply the contents of the port by a specific number.

 

Multiplying port contents by a number

And this is the number by which we multiply. The “~” sign indicates inversion. In our case, the inverted unit is zero. That is, we multiply the contents of the port by zero, shifted by 5 bits. For example, it was 10110001, it became 10100001. The remaining bits remained unchanged.

Multiply the contents of the port by zero shifted by 5 bits

The same can be done using the invert operation (^):

Reading from ports, the analog of digitalRead () is performed using the PIN register, in practice it looks like this:

Read from ports

Here we check whether the expression in parentheses is equal to the real state of the ports, i.e. similarly if we wrote if (digitalRead (12) == 1).


Conclusion

Why are there such difficulties with port management if you can use standard convenient functions? It's all about speed and code size. When using the second method, discussed in the article, the code size is significantly reduced, and the speed increases by several orders of magnitude. The standard digitalWrite () was performed in 1800 μs, and recording directly to the port in 0.2 μs, and digitalRead () in 1900 μs, and also became 0.2 μs. This control method was found on the open spaces of the network and is often found in code. finished projects.

See also at i.electricianexp.com:

  • Connecting and programming Arduino for beginners
  • How to connect incremental encoder to Arduino
  • PIC microcontrollers for beginners
  • Microcontroller Remote Control: IR Remote, Arduino, ESP8266, 433 ...
  • Measuring temperature and humidity on Arduino - a selection of ways

  •  
     
    Comments:

    # 1 wrote: Kipovets | [quote]

     
     

    "But with logical addition, a problem arises - you cannot turn the unit into zero, because:

    0 + 0 = 1 "(c)

    Small oversight: 0 + 0 = 0.

     
    Comments:

    # 2 wrote: chugou | [quote]

     
     

    Kipovets, he probably wanted to say:

    1 + 1 = 1

     
    Comments:

    # 3 wrote: | [quote]

     
     

    Kipovets,
    Banal typo! You see how good it is that specialists are sitting on our portal! You have to make only suitable content!

     
    Comments:

    # 4 wrote: Serg | [quote]

     
     

    In the final part, it says PORTB | = 1 << 5 ... if (digitalRead (12) == 1). But 5 pin of port B, it is 13 pin of arduino. Or I'm wrong?!

     
    Comments:

    # 5 wrote: p-a-h-a | [quote]

     
     

    If (PINB == B00010000) {} is notsimilar to if we wrote if (digitalRead (12) == 1)
    rather analog
    (digitalRead (12) == 1) &&(digitalRead (13) == 1) &&(digitalRead (14) == 1) &&(digitalRead (15) == 1) &&(digitalRead (11) == 1) ... and so 8 port pins

    Here you need either this:
    if (
    ! (~ PORTB & (1 << PB4))) {} //returns0 or 1
    either like this:
    if (PORTB & (1 << PB4)) {} // returns the shifted unit = 16, DEC
    either like this:
    if (
    bit_is_set (PORTB, 4)) {}// returns the shifted unit = 16, DEC