Difference between revisions of "AVR Embedded Tutorial - Simple GPIO on and off output"

From Lazarus wiki
Jump to navigationJump to search
(Marked for deleting)
m (English translation of German page)
 
Line 1: Line 1:
 
{{LanguageBar}}
 
{{LanguageBar}}
  
There is no English version of this article.
+
= Easy GPIO input and output =
  
[[Category:Pages for deletion]]
+
== Direct port manipulation ==
 +
 
 +
Before you can switch a GPIO pin x to HIGH, you have to configure it as an output. This is done using '''DDR'''x.
 +
 
 +
* Configure pin as an output:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  DDRx := DDRx or (1 shl Pinx);
 +
</syntaxhighlight>
 +
 
 +
* Set pin to be HIGH:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  PORTx := PORTx or (1 shl Pinx);
 +
</syntaxhighlight>
 +
 
 +
* Set pin to be LOW:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  PORTx := PORTx and not (1 shl Pinx);
 +
</syntaxhighlight>
 +
 
 +
* Switch pin (high to low or low to high):
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  PORTx := PORTx xor (1 shl Pinx);
 +
</syntaxhighlight>
 +
 
 +
If you leave '''DDR'''x LOW, a pull-up resistor is enabled using '''PORT'''x. This is needed to connect a push button to GND. So you can do without an external pull-up resistor.
 +
 
 +
The status of the GPIO pin x can be queried using '''PIN'''x.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  if PINx and (1 shl Pinx) > 0 then
 +
    Pin_is_HIGH;
 +
</syntaxhighlight>
 +
 
 +
== Blink example ==
 +
 
 +
This example shows how to address a GPIO with an ATtiy2313. For this, two LEDs are connected to pins 0 and 1 of Port D.
 +
 
 +
=== Alternating indicator ATtiny2313 ===
 +
 
 +
<syntaxhighlight lang="pascal">
 +
program Project1;
 +
const
 +
  DP0 = 0;      // Pin 0 PortD
 +
  DP1 = 1;      // Pin 1 PortD
 +
  sl = 150000;  // Delay
 +
 
 +
procedure mysleep(t: int32);  // A simple delay
 +
var
 +
  i: Int32;
 +
begin
 +
  for i := 0 to t do
 +
    asm
 +
      nop
 +
    end;
 +
end;
 +
 
 +
begin
 +
  // Configure pin 0 and 1 of Port D as output
 +
  DDRD := DDRD or (1 shl DP0) or (1 shl DP1);
 +
 
 +
  repeat
 +
    // change LED
 +
    PORTD := PORTD or (1 shl DP0);      // Pin 0 on (high)
 +
    PORTD := PORTD and not (1 shl DP1);  // Pin 1 off (low)
 +
    mysleep(sl);                        // Delay
 +
 
 +
    // change LED
 +
    PORTD := PORTD or (1 shl DP1);      // Pin 1 on (high)
 +
    PORTD := PORTD and not (1 shl DP0);  // Pin 0 off (low)
 +
    mysleep(sl);                        // Delay
 +
  until 1 = 2;
 +
end.
 +
</syntaxhighlight>
 +
 
 +
=== Toggle pin ATtiny2313 ===
 +
 
 +
If you want to switch the pin, no matter what its original state, you can use an '''xor''' operator. This also works for an alternating high/low signal. '''DP0''' is first set to HIGH.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  begin
 +
  // Configure Pin 0 and 1 of Port D as output
 +
  DDRD  := DDRD or (1 shl DP0) or (1 shl DP1);
 +
  PORTD := PORTD or (1 shl DP0);    // Pin 0 on (high)
 +
 
 +
  repeat
 +
    // Blink LED
 +
    PORTD := PORTD xor (1 shl DP0);  // Switch pin 0
 +
    PORTD := PORTD xor (1 shl DP1);  // Switch pin 1
 +
    mysleep(sl);                    // Delay
 +
  until 1 = 2;
 +
end.
 +
</syntaxhighlight>
 +
 
 +
== Simplify port access ==
 +
 
 +
If you access ports and do not always want to write the ''and'', or '''and xor'' operators, you can also use the following procedures/functions. The examples below show this for PORT D. You can easily adjust these for other ports.
 +
 
 +
=== pinMode ===
 +
 
 +
This corresponds roughly to '''pinMode(...''' from the Arduino.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure ModePortD(Pin: byte; Value: Boolean);
 +
  begin
 +
    if Value then
 +
      begin
 +
        DDRD := DDRD or (1 shl pin);
 +
      end
 +
    else
 +
      begin
 +
        DDRD := DDRD and not (1 shl pin);
 +
      end;
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
=== Digital write ===
 +
 
 +
This corresponds approximately to '''digitalWrite(...''' from the Arduino.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure WritePortD(Pin: byte; Value: Boolean);
 +
  begin
 +
    if Value then
 +
      begin
 +
        PORTD := PORTD or (1 shl pin);
 +
      end
 +
    else
 +
      begin
 +
        PORTD := PORTD and not (1 shl pin);
 +
    end;
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
=== Digital switch ===
 +
 
 +
Switches the pin, there is no equal function from the Arduino.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
procedure WritePortD(Pin: byte);
 +
  begin
 +
    PORTD := PORTD xor (1 shl pin);
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
=== Digital read ===
 +
 
 +
This corresponds approximately to '''digitalRead(...''' from the Arduino.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
function ReadPortD(bit: byte): Boolean;
 +
begin
 +
  Result := PIND and (1 shl bit) <> 0;
 +
end;
 +
</syntaxhighlight>
 +
 
 +
== Address pin directly ==
 +
 
 +
If you connect an absolute bit-packed array, record or set to the port, you can access each pin directly, without annoying '''or'' and '''and not''' operators.
 +
 
 +
The compiled code is exactly the same size as the conventional method with '''or''' and '''and not'''. It could hardly be more elegant.
 +
 
 +
The following three examples address the first 4 pins from PORT D which control 4 LEDs.
 +
 
 +
=== Bitpacked array ===
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
  LEDPort: bitpacked array[0..7] of boolean absolute PORTD;
 +
 
 +
begin
 +
  LEDPort [0] := True;
 +
  LEDPort [1] := True;
 +
  LEDPort [2] := False;
 +
  LEDPort [3] := False;
 +
</syntaxhighlight>
 +
 
 +
=== Bitpacked record ===
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
  LEDPort: bitpacked record
 +
    red, green, yellow, blue: boolean;
 +
  end absolute PORTD;
 +
 
 +
begin
 +
  LEDPort.red    := True;
 +
  LEDPort.green  := True;
 +
  LEDPort.yellow := False;
 +
  LEDPort.blue  := False;
 +
</syntaxhighlight>
 +
 
 +
=== Set ===
 +
 
 +
<syntaxhighlight lang="pascal">
 +
var
 +
  LEDPortS: set of (green, yellow, red, blue) absolute PORTD;
 +
 
 +
begin
 +
  LEDPortS := [yellow, red];                    // yellow, red on
 +
  LEDPortS := LEDPortS + [green, blue] - [red];  // green, blue on; red off
 +
  LEDPortS := LEDPortS - [green, blue] + [red];  // green, blue off; red on
 +
</syntaxhighlight>
 +
 
 +
=== DDR and PORT ===
 +
 
 +
'''DDR'''x can be solved in a similar way to '''PORT'''x. It is best to declare the LEDs as a type:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
Type
 +
  TLed = bitpacked record
 +
    green, yellow, red, blue: boolean;
 +
end;
 +
 
 +
var
 +
  LedPORT: TLed absolute PORTD;
 +
  LedDDR: TLed absolute DDRD;
 +
 
 +
begin
 +
  LedDDR.green := True;
 +
  LedDDR.red  := True;
 +
 
 +
  repeat
 +
    LedPORT.green := True;
 +
    LedPORT.red  := False;
 +
    ...
 +
</syntaxhighlight>
 +
 
 +
'''PIN'''x can be solved in a similar way.
 +
 
 +
== Arduino pins PD0 and PD1 ==
 +
 
 +
This description refers to the Arduino Uno/Nano, other AVRs may also be affected.
 +
 
 +
The PORTD '''PD0''' and '''PD1''' pins are also used for the UART interface by default and are therefore not usable. If you still want to use these pins, you have to disable their UART functions.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
  UCSR0B := 0;    // Disable UART functions
 +
</syntaxhighlight>
 +
 
 +
Note: The LEDs '''RX''' and '''TX''' will light in the opposite way because the anode of these LEDs is directly connected to Vcc.
 +
 
 +
For more information about the UART, see [[AVR Embedded Tutorial - UART|UART]] - serial input and output via UART (COM port).
 +
 
 +
== See also ==
 +
 
 +
* [[AVR Embedded Tutorial| AVR Embedded Tutorials]] - Overview
 +
 
 +
[[Category:AVR]]
 +
[[Category:Arduino]]
 +
[[Category:Embedded]]
 +
[[Category:Tutorials]]

Latest revision as of 02:22, 26 January 2020

Deutsch (de) English (en)

Easy GPIO input and output

Direct port manipulation

Before you can switch a GPIO pin x to HIGH, you have to configure it as an output. This is done using DDRx.

  • Configure pin as an output:
  DDRx := DDRx or (1 shl Pinx);
  • Set pin to be HIGH:
  PORTx := PORTx or (1 shl Pinx);
  • Set pin to be LOW:
  PORTx := PORTx and not (1 shl Pinx);
  • Switch pin (high to low or low to high):
  PORTx := PORTx xor (1 shl Pinx);

If you leave DDRx LOW, a pull-up resistor is enabled using PORTx. This is needed to connect a push button to GND. So you can do without an external pull-up resistor.

The status of the GPIO pin x can be queried using PINx.

  if PINx and (1 shl Pinx) > 0 then 
    Pin_is_HIGH;

Blink example

This example shows how to address a GPIO with an ATtiy2313. For this, two LEDs are connected to pins 0 and 1 of Port D.

Alternating indicator ATtiny2313

 program Project1;
 const
   DP0 = 0;      // Pin 0 PortD
   DP1 = 1;      // Pin 1 PortD
   sl = 150000;  // Delay

 procedure mysleep(t: int32);  // A simple delay
 var
   i: Int32;
 begin
   for i := 0 to t do
     asm 
       nop
     end;
 end;

 begin
   // Configure pin 0 and 1 of Port D as output
   DDRD := DDRD or (1 shl DP0) or (1 shl DP1);

   repeat
     // change LED
     PORTD := PORTD or (1 shl DP0);       // Pin 0 on (high)
     PORTD := PORTD and not (1 shl DP1);  // Pin 1 off (low)
     mysleep(sl);                         // Delay

     // change LED
     PORTD := PORTD or (1 shl DP1);       // Pin 1 on (high)
     PORTD := PORTD and not (1 shl DP0);  // Pin 0 off (low)
     mysleep(sl);                         // Delay
   until 1 = 2;
 end.

Toggle pin ATtiny2313

If you want to switch the pin, no matter what its original state, you can use an xor operator. This also works for an alternating high/low signal. DP0 is first set to HIGH.

  begin
   // Configure Pin 0 and 1 of Port D as output
   DDRD  := DDRD or (1 shl DP0) or (1 shl DP1);
   PORTD := PORTD or (1 shl DP0);     // Pin 0 on (high)

   repeat
     // Blink LED
     PORTD := PORTD xor (1 shl DP0);  // Switch pin 0
     PORTD := PORTD xor (1 shl DP1);  // Switch pin 1
     mysleep(sl);                     // Delay
   until 1 = 2;
 end.

Simplify port access

If you access ports and do not always want to write the and, or 'and xor operators, you can also use the following procedures/functions. The examples below show this for PORT D. You can easily adjust these for other ports.

pinMode

This corresponds roughly to pinMode(... from the Arduino.

procedure ModePortD(Pin: byte; Value: Boolean);
  begin
    if Value then 
      begin
        DDRD := DDRD or (1 shl pin);
      end
    else 
      begin
        DDRD := DDRD and not (1 shl pin);
      end;
  end;

Digital write

This corresponds approximately to digitalWrite(... from the Arduino.

procedure WritePortD(Pin: byte; Value: Boolean);
  begin
    if Value then 
      begin
        PORTD := PORTD or (1 shl pin);
      end
    else 
      begin
        PORTD := PORTD and not (1 shl pin);
    end;
  end;

Digital switch

Switches the pin, there is no equal function from the Arduino.

procedure WritePortD(Pin: byte);
  begin
    PORTD := PORTD xor (1 shl pin);
  end;

Digital read

This corresponds approximately to digitalRead(... from the Arduino.

 function ReadPortD(bit: byte): Boolean;
 begin
   Result := PIND and (1 shl bit) <> 0;
 end;

Address pin directly

If you connect an absolute bit-packed array, record or set to the port, you can access each pin directly, without annoying or and and not' operators.

The compiled code is exactly the same size as the conventional method with or and and not. It could hardly be more elegant.

The following three examples address the first 4 pins from PORT D which control 4 LEDs.

Bitpacked array

 var
   LEDPort: bitpacked array[0..7] of boolean absolute PORTD;

 begin
   LEDPort [0] := True;
   LEDPort [1] := True;
   LEDPort [2] := False;
   LEDPort [3] := False;

Bitpacked record

 var
   LEDPort: bitpacked record
     red, green, yellow, blue: boolean;
   end absolute PORTD;

 begin
   LEDPort.red    := True;
   LEDPort.green  := True;
   LEDPort.yellow := False;
   LEDPort.blue   := False;

Set

 var
   LEDPortS: set of (green, yellow, red, blue) absolute PORTD;

 begin
   LEDPortS := [yellow, red];                     // yellow, red on
   LEDPortS := LEDPortS + [green, blue] - [red];  // green, blue on; red off
   LEDPortS := LEDPortS - [green, blue] + [red];  // green, blue off; red on

DDR and PORT

DDRx can be solved in a similar way to PORTx. It is best to declare the LEDs as a type:

 Type
   TLed = bitpacked record
     green, yellow, red, blue: boolean;
 end;

 var
   LedPORT: TLed absolute PORTD;
   LedDDR: TLed absolute DDRD;

 begin
   LedDDR.green := True;
   LedDDR.red   := True;

   repeat
     LedPORT.green := True;
     LedPORT.red   := False;
     ...

PINx can be solved in a similar way.

Arduino pins PD0 and PD1

This description refers to the Arduino Uno/Nano, other AVRs may also be affected.

The PORTD PD0 and PD1 pins are also used for the UART interface by default and are therefore not usable. If you still want to use these pins, you have to disable their UART functions.

  UCSR0B := 0;    // Disable UART functions

Note: The LEDs RX and TX will light in the opposite way because the anode of these LEDs is directly connected to Vcc.

For more information about the UART, see UART - serial input and output via UART (COM port).

See also