?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print

참조 : https://www.arduino.cc/en/Reference/PortManipulation


출처 : https://arsviator.blogspot.kr/2010/06/%EC%95%84%EB%92%A4%EB%85%B8%EC%97%90%EC%84%9C-%ED%8F%AC%ED%8A%B8%EB%A5%BC-%EC%A7%81%EC%A0%91-%EC%96%B5%EC%84%B8%EC%8A%A4-direct-port.html

출처2 : http://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/


아뒤노에서도 포트 레지스터를 사용하면 마이크로 컨트롤러의 I/O핀을 로우레벨에서 고속으로 조작할 수 있다. 아뒤노 보드(ATmega8과 ATmega168)에 사용된 칩은 3개의 포트를 가지고 있다.

  • B (digital pin 8~13)
  • C (analog input pins)
  • D (digital pin 0~7)


각 포트는 3개의 레지스터로 제어되고 레지스터들은 아뒤노에 변수로 정의되어 있다. DDR 레지스터는 각 핀이 INPUT 또는 OUTPUT인지를 결정한다. PORT레지스터는 핀의 출력값을 HIGH또는 LOW로 제어한다. PIN레지스터는 INPUT모드인 핀의 상태값을 읽는다.

DDR과 PORT레지스터는 읽고 쓰기가 가능하다. PIN레지스터는 입력 상태에 대응하며 읽기 전용이다.

_MWANMgftkEz0nOWln2JrGW7coqK7o1Qh9LgYxEiDATQSZTKqE4_Mpj5kZqq4VvWGgmhDfSByBsKvORR9eoMx87RXKwblNQ0lqFjGY0=s0-d



PORTD는 아뒤노의 digital pin 0~7에 매핑된다.

  •   DDRD - 포트 D 데이터 방향 레지스터 (R/W)
  •   PORTD - 포트 D 데이터 레지스터 (R/W)
  •   PIND - 포트 D 입력 핀 레지스터 (R)


PORTB는 아뒤노의 digital pin 8~13에 매핑된다. 상위 2 비트는 crystal핀에 매핑되고 사용할 수 없다.

  •   DDRB - 포트 B 데이터 방향 레지스터 (R/W)
  •   PORTB - 포트 B 데이터 레지스터 (R/W)
  •   PINB - 포트 B 입력 핀 레지스터 (R)


PORTD는 아뒤노의 analog pin 0~5에 매핑된다. 핀 6, 7은 아뒤노 미니에서만 억세스할 수 있다.

  •   DDRC - 포트 C 데이터 방향 레지스터 (R/W)
  •   PORTC - 포트 C 데이터 레지스터 (R/W)
  •   PINC - 포트 C 입력 핀 레지스터 (R)

 
이 레지스터들의 각 비트는 핀 하나에 대응한다. 아뒤노 핀 번호와 포트/비트간의 전체 매핑은 칩의 다이어그램을 보면 된다. (포트의 일부 비트는 I/O이외의 용도로 사용될 수도 있기 때문에 그에 해당하는 레지스터 비트 값을 바꾸지 않도록 주의해야 한다.)

예제

위의 핀 맵을 참조하면 PortD 레지스터는 아뒤노 디지털 핀 0~7을 제어한다.

하지만 핀 0과 1은 아뒤노 프로그래밍과 디버깅을 위한 시리얼 통신에 사용되기 때문에 시리얼 입/출력 함수를 위해 필요하지 않는 이상 이 핀을 바꾸는건 피하는게 좋다. 이 값을 잘못 바꾸면 프로그램 다운로드나 디버깅을 방해받을 수 있다.

DDRD는 포트 D의 방향 레지스터이다. 레지스터의 각 비트는 포트 D의 핀이 INPUT 또는 OUTPUT으로 설정되도록 제어한다. 

DDRD = B11111110;  // 아뒤노 핀 1~7은 출력으로, 핀 0는 입력으로 설정
DDRD = DDRD | B11111100;  // 더 안전한 방법. Rx/Tx로 사용되는 핀0과 1의 값은 변경하지 않고 핀 2~7을 출력으로 설정

PORTD는 출력 상태를 위한 레지스터이다. 

PORTD = B10101000;  // 디지털 핀 7,5,3을 HIGH로 만듬

DDRD레지스터 또는 pinMode()를 사용해 저 핀들을 OUTPUT으로 설정해 놓은 경우 핀에서 5V가 출력되는걸 볼 수 있다.

PIND는 입력 레지스터 변수로 모든 디지털 입력 핀의 값을 동시에 읽는다.

포트 조작 팁

#define B0 B00000001
#define B1 B00000010
#define B2 B00000100
#define B3 B00001000
#define B4 B00010000
#define B5 B00100000
#define B6 B01000000
#define B7 B10000000

이렇게 비트를 정의해 놓으면 여러 핀을 동시에 H로 만들 경우는

PORTD |= B7 | B5 | B4;  // 디지털 핀 7, 5, 4를 HIGH로

여러 핀을 동시에 L로 만들 경우는

PORTD &= ~(B7 | B5 | B4);  // 디지털 핀 7, 5, 4를 LOW로

여러 핀의 값을 토글(현재값이 H면 L로, 현재값이 L이면 H로) 시키는 경우는

PORTD ^= B7 | B5 | B4;  // 디지털 핀 7, 5, 4의 값을 토글

Port manipulation을 사용하는 이유

일반적으로 이런식으로 포트를 직접 조작하는건 별로 좋은 생각이 아니다. 그 이유는 

  • 코드 디버깅 및 관리하기가 더 힘들어지고 다른 사람이 이해하기 힘들다. 마이크로 프로세서가 코드를 실행하는건 몇 마이크로초밖에 안 걸리지만 그 코드가 왜 동작하지 않는가 찾아내고 수정하는데는 몇시간이 걸린다. 시간이 중요하지 않은가? 컴퓨터의 시간은 사용전력으로 따져보면 매우 싸다. 가능한 가장 확실한 방법으로 코드를 작성하는게 좋다.
  • 코드의 이식성이 떨어진다. digitalRead()와 digitalWrite()를 사용하면 모든 Atmel 마이크로 컨트롤러에서 실행시키는게 훨씬 쉽지만 컨트롤과 포트 레지스터는 각 마이크로컨트롤러마다 다를 수 있어 코드를 바로 사용하기 힘들수 있다.
  • 직접 포트 레지스터를 억세스하다 보면 의도하지 않은 문제를 만들기 쉬워진다. 위의 예제에서 DDRD = B11111110; 에서처럼 핀 0은 입력으로 남겨둬야만 한다. 핀 0는 시리얼 포트의 Rx이다. 하지만 실수로 핀 0을 OUTPUT으로 만들어버리면 갑자기 시리얼포트가 동작하지 않게 만들기 쉽다. 갑자기 시리얼 포트에서 데이터를 받을 수 없으면 매우 혼란스러울 것이다.


그럼에도 불구하고 왜 포트 레지스터를 직접 조작해야 하는가? 포트 레지스터를 직접 억세스하면 다음의 장점이 있다.

  • 핀의 출력을 H/L로 매우 빠르게(몇 나노초 이내에) 바꾸는게 필요한 경우가 있다. lib/targets/arduino/wiring.c 소스코드를 보면 digitalRead()와 digitalWrite()는 각각 몇십줄의 코드로 된 걸 볼 수 있는데 이는 컴파일되면 상당한 양의 기계어 명령어가 된다. 각 기계어 명령어는 16MHz 클럭사이클에서 한 클럭사이클이 필요한데 시간에 민감한 어플리케이션에서는 상당한 부담이 된다. 그에 비해 포트를 직접 억세스하면 훨씬 적은 클럭 사이클로 동일한 작업을 할 수 있다.
  • 때로는 여러개의 출력핀의 값을 정확히 동시에 바꿔줘야 할 필요가 있다. digitalWrite(10, HIGH); 다음에 digitalWrite(11, HIGH)를 호출하면 먼저 핀 10이 HIGH로 바뀌고 몇 마이크로초 후에 핀 11이 HIGH로 바뀌게 된다. 때로는 외부의 시간에 민감한 디지털 회로의 동작에 혼란을 줄 수 있다. 이 경우 PORTB |= B1100; 을 사용하면 동시에 두 핀을 HIGH로 바꿔줄 수 있다.
  • 프로그램 메모리가 부족한 경우 이 방식을 사용하면 코드 크기를 작게 줄일 수 있다. 루프를 사용해 각 핀을 독립적으로 조작하는것에 비해 포트 레지스터를 통해 동시에 여러 하드웨어 핀에 값을 쓰는게 훨씬 작은 명령어를 사용하게 된다. 때로는 이 방식을 사용여부가 프로그램이 플래쉬 메모리에 들어갈 수 있는가 못들어가는가를 결정하는 차이가 될 수도 있다.

------------------------------------------


Tutorial: Arduino Port Manipulation

Control Arduino I/O pins faster and with less code in chapter forty-three of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – a series of articles on the Arduino universe.

[Updated 19/01/13]

In this article we are going to revisit the I/O pins, and use what is called “Port Manipulation” to control them in a much faster manner than using digitalWrite()/digitalRead().

Why?

Speed! Using this method allows for much faster I/O control, and we can control or read groups of I/O pins simultaneously, not one at a time;

Memory! Using this method reduces the amount of memory your sketch will use.

Once again I will try and keep things as simple as possible. This article is written for Arduino boards that use the ATmega168 or ATmega328 microcontrollers (used in Arduino Duemilanove/Uno, Freetronics Eleven/EtherTen, etc).

First, we’ll use the I/O as outputs. There are three port registers that we can alter to set the status of the digital and analogue I/O pins. A port register can be thought of  as a special byte variable that we can change which is read by the microcontroller, therefore controlling the state of various I/O ports. We have three port registers to work with:

  • D – for digital pins seven to zero (bank D)
  • B – for digital pins thirteen to eight (bank B)
  • C – for analogue pins five to zero (bank … C!)

Register C can control analogue pins seven to zero if using an Arduino with the TQFP style of ATmega328, such as the Nano or Freetronics EtherTen). For example:

It is very simple to do so. In void setup(), we use

1
DDRy = Bxxxxxxxx

where y is the register type (B/C/D) and xxxxxxxx are eight bits that determine if a pin is to be an input or output. Use 0 for input, and 1 for output. The LSB (least-significant bit [the one on the right!]) is the lowest pin number for that register. Next, to control a bank of pins, use

1
PORTy = Bxxxxxxxx

where y is the register type (B/C/D) and xxxxxxxx are eight status bits – 1 for HIGH, 0 for LOW. This is demonstrated in the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Example 43.1
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// Digital 0~7 set to outputs, then on/off using port manipulation
 
void setup()
{
  DDRD = B11111111; // set PORTD (digital 7~0) to outputs
}
 
void loop()
{
  PORTD = B11110000; // digital 4~7 HIGH, digital 3~0 LOW
  delay(1000);
  PORTD = B00001111; // digital 4~7 LOW, digital 3~0 HIGH
  delay(1000);
}

It sets digital pins 7~0 to output in void setup(). Then it alternates turning on and off alternating halves of digital pins 0~7. At the start I mentioned that using port manipulation was a lot faster than using regular Arduino I/O functions. How fast? To test the speed of port manipulation vs. using digitalWrite(), we will use the following circuit:

… and analyse the output at digital pins zero and seven using a digital storage oscilloscope. Our first test sketch turns on and off digital pins 0~7 without any delay between PORTD commands – in other words, as fast as possible. The sketch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Example 43.1.1
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// Digital 0~7 set to outputs, then on/off using port manipulation
 
void setup()
{
  DDRD = B11111111; // set PORTD (digital 7~0) to outputs
}
 
void loop()
{
  PORTD = B11111111;
  PORTD = B00000000;
}

In the image below, digital zero is channel one, and digital seven is channel three:

testa

Wow – check the frequency measurements – 1.1432 MHz! Interesting to note the longer duration of time when the pins are low vs. high.

[Update] Well it turns out that the extra time in LOW includes the time for the Arduino to go back to the top of void loop(). This can be demonstrated in the following sketch. We turn the pins on and off five times instead of once:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Example 43.1.2
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
 
void setup()
{
  DDRD = B11111111; // set PORTD (digital 7~0) to outputs
}
 
void loop()
{
  PORTD = B11111111;
  PORTD = B00000000;
  PORTD = B11111111;
  PORTD = B00000000;
  PORTD = B11111111;
  PORTD = B00000000;
  PORTD = B11111111;
  PORTD = B00000000;
  PORTD = B11111111;
  PORTD = B00000000;
}

And the results from the MSO. You can see the duty cycle is much closer to 50% until the end of the sketch, at which point around 660 nanoseconds is the time used between the end of the last LOW period and the start of the next HIGH:

update1

Next we do it the normal way, using this sketch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Example 43.2
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// Digital 0~7 set to outputs, then on/off using digitalWrite()
 
void setup()
{
  for (int a=0; a<8; a++)
  {
    pinMode(a, OUTPUT);
  }
}
 
void loop()
{
  for (int a=0; a<8; a++)
  {
    digitalWrite(a, HIGH);
  }
  for (int a=0; a<8; a++)
  {
    digitalWrite(a, LOW);
  }
}

And the results:

testb

That was a lot slower – we’re down to 14.085 kHz, with a much neater square-wave output. Could some CPU time be saved by not using the for loop? We tested once more with the following sketch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Example 43.3
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// Digital 0~7 set to outputs, then on/off using individual digitalWrite()
 
void setup()
{
  for (int a=0; a<8; a++)
  {
    pinMode(a, OUTPUT);
  }
}
 
void loop()
{
  digitalWrite(0, HIGH);
  digitalWrite(1, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(0, LOW);
  digitalWrite(1, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);
}

and the results:

testc

A small speed boost, the frequency has increased to 14.983 kHz. Hopefully you can now understand the benefits of using port manipulation. However there are a few things to take note of:

  • You can’t control digital pins 0 and 1 (in bank D) and use the serial monitor/port. For example if you set pin zero to output, it can’t receive data!
  • Always document your sketch – take pity on others who may need to review it later on and become puzzled about wchich bits are controlling or reading what!
Now to waste some electron flows by blinking LEDs. Using the circuit described earlier, the following sketch will create various effects for someone’s enjoyment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Example 43.4
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// Fun with 8 LEDs on digital 7~0
 
void setup()
{
  DDRD = B11111111; // set PORTD (digital 7~0)
  //  to output
}
 
byte a = B11111111;
byte b = B00000001;
byte c = B10000000;
byte e = B10101010;
 
void krider()
{
  for (int k=0; k<5; k++)
  {
    for (int z=0; z<8; z++)
    {
      PORTD = b << z;
      delay(100);
    }
 
    for (int z=0; z<8; z++)
    {
      PORTD = c >> z;
      delay(100);
    }
  }
}
 
void onOff()
{
  for (int k=0; k<10; k++)
  {
    PORTD = a;
    delay(100);
    PORTD = 0;
    delay(100);
  }
}
 
void invBlink()
{
  for (int z=0; z<10; z++)
  {
    PORTD = e;
    delay(100);
    PORTD = ~e;
    delay(100);
  }
}
 
void binaryCount()
{
  for (int z=0; z<256; z++)
  {
    PORTD = z;
    delay(100);
  }
  PORTD=0;
}
 
void loop()
{
  invBlink();
  delay(500);
  binaryCount();
  delay(500);
  krider();
  delay(500);
  onOff();
}

And here it is in real life:

Now to use the I/O pins as inputs. Again, it is very simple to do so. In void setup(), we use

1
DDRy = Bxxxxxxxx

where y is the register type (B/C/D) and xxxxxxxx are eight bits that determine if a pin is to be an input or output. Use 0 for input. The LSB (least-significant bit [the one on the right!]) is the lowest pin number for that register. Next, to read the status of the pins we simply read the byte:

1
PINy

where y is the register type (B/C/D). So if you were using port B as inputs, and digital pins 8~10 were high, and 11~13 were low, PINB would be equal to B00000111. Really, that’s it!

Now for another demonstration using both inputs and outputs. We will use a push-wheel switch from Chapter 40 on our inputs (digital pins 8~11), and a seven segment LED display for output (on digtal pins 7~0 – segments dp then a~f). The following sketch reads the input from the switch, which returns 0~9 in binary-coded decimal. This value is then used in the function void disp() to retrieve the matching byte from the array “segments”, which contains the appropriate outputs to drive the seven segment LED display unit. Here is the sketch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Example 43.5
// tronixstuff.com/tutorials > chapter 43
// John Boxall - October 2011
// inputs and outputs
 
byte segments[] = {
  B01111110, B00110000, B01101101, B01111001, B00110011, B01011011, B01011111, B01110000, B01111111, B01111011};
// digital pins 7~0 connected to display pins dp,a~g
void setup()
{
  DDRB = B00000000; // set PORTB (digital 13~8) to inputs
  DDRD = B11111111; // set PORTD (digital 7~0) to outputs
}
 
void disp(int z)
{
  PORTD = segments[z];
}
 
void loop()
{
    disp(PINB);
    delay(100);
}

And the ubiquitous demonstration video:


By now I hope you have an understanding of using port manipulation for your benefit. With a little effort your sketches can be more efficient in terms of speed and memory space, and also allow nifty simultaneous reading of input pins.

LEDborder

Have fun and keep checking into tronixstuff.com. Why not follow things on twitterGoogle+, subscribe  for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other –  and we can all learn something.




  • Dreamy 2018.06.29 16:24

    ATTINY85 포트 직접제어하기

    http://www.instructables.com/id/ATTiny-Port-Manipulation/


Dreamy의 코드 스크랩

내가 모으고 내가 보는

List of Articles
번호 분류 제목 날짜 조회 수 추천 수
461 LINUX gdb 쓸때 상용구 secret 2019.06.26 0 0
460 Pi 라즈베리파이 내 작업 명령 secret 2019.06.10 0 0
459 일반 범용 레지 스터(eax, ebx, ecx, edx, esi, edi, esp, ebp) 2019.06.03 9696 0
458 Python 디렉토리 없으면 만들기 2019.03.30 5719 0
457 Python 줄 바꿈 없이 출력하는 방법 2019.03.30 6800 0
456 LINUX Ubuntu에서 Python 버전을 변경하는 방법 2019.03.29 11958 0
455 Python Google Colab에서 파일 업로드/다운로드 팁 2019.03.06 36405 0
454 Python pandas, matplot 자주사용하는 코드 2019.03.06 4882 0
453 Python matplot에서 한글이 보이도록 하는 코드 2019.03.06 6019 0
452 일반 OMV (OpenMediaVault) 플러그인들 2019.02.10 7926 0
451 일반 ddns 정보 secret 2019.02.10 0 0
450 Android git archive 를 사용해서 폴더를 .git 제외하고 tar 나 zip 으로 묶기 1 2019.02.10 7028 0
449 LINUX vi 설정하기(.vimrc) 2019.02.01 5343 0
448 Pi 칩 저항 사이즈표, 사이즈변환, 와트, 오차표 2019.01.11 10421 0
447 Pi NPN과 PNP 트랜지스터의 차이 2019.01.03 13846 0
목록
Board Pagination ‹ Prev 1 2 3 4 5 6 7 8 9 10 ... 34 Next ›
/ 34

나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소

Designed by sketchbooks.co.kr / sketchbook5 board skin

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5