Arduino에서 직렬 통신은 어떻게 작동합니까?


16

Arduino Uno, Mega2560, Leonardo 및 유사한 보드를 참조하십시오.

  • 직렬 통신은 어떻게 작동합니까?
  • 시리얼은 얼마나 빠릅니까?
  • 발신자와 수신자를 어떻게 연결합니까?

참고 :이 질문은 참조 질문입니다.


: 당신은 나노 둘 사이에 그냥 일반 USB 프로그래밍 케이블을 사용하여 파이썬 데이터 로거를 실행하는 Raspian 시스템에 연결된 양쪽에 버퍼에 대한이 흥미를 찾을 수 arduino.stackexchange.com/questions/11710/...을
SDsolar

답변:


16

비동기 직렬 (일반적으로 직렬이라고 함) 통신은 한 장치에서 다른 장치로 바이트를 보내는 데 사용됩니다. 장치는 다음 중 하나 이상일 수 있습니다.

  • 아두 이노
  • PC
  • GPS
  • RFID 카드 리더
  • LCD 디스플레이
  • 모뎀
  • 다른

클럭 속도 및 데이터 샘플링

SPI / USB / I2C 직렬 통신과 달리 클럭 신호는 없습니다. 샘플링 클럭은 합의 된 샘플링 속도 (보드 율이라고도 함)입니다. 송신자와 수신자 모두 동일한 속도를 사용하도록 구성되어야합니다. 그렇지 않으면 수신자는 의미없는 데이터를 수신합니다 (비트가 전송 된 것과 동일한 속도로 샘플링되지 않기 때문에).

전송은 비동기 적 이며 기본적으로 바이트는 간격이 다양하여 언제든지 보낼 수 있습니다. 이 그래픽은 전송되는 단일 바이트를 보여줍니다.

직렬 통신-1 바이트 전송

위의 그래픽은 문자 'F'가 전송되는 것을 보여줍니다. ASCII에서 이것은 0x46 (16 진) 또는 0b01000110 (2 진)입니다. 적어도 상당한 (하위) 비트는 도착 순서로 비트를 참조 따라서 상기 그래픽에 먼저 전송된다 01100010.

바이트 사이의 "유휴"시간은 연속 "1"비트로 전송됩니다 (효과적으로 전송 라인은 지속적으로 높게 유지됩니다).

바이트의 시작을 나타 내기 위해 시작 비트 는 항상 그림과 같이 줄을 낮게 잡아 당겨 표시합니다. 수신기가 시작 비트를 확인하면 샘플 시간의 1.5 배를 기다린 다음 데이터 비트를 샘플링합니다. 다음과 같이 1.5 배 기다립니다.

  • 시작 비트를 건너 뜁니다
  • 다음 비트 중간에 샘플

예를 들어 전송 속도가 9600 전송 속도 인 경우 샘플 속도는 1/9600 = 0.00010416초 (104.16 µs)입니다.

따라서 9600 보드에서 시작 비트를 수신 한 후 수신기는 156.25 µs 동안 기다린 다음 104.16 µs마다 샘플링합니다.

비트 타이밍 시작

정지 비트 의 목적은 각 바이트 사이에 반드시 1 비트 가 있도록하는 것입니다. 정지 비트가 없으면 바이트가 0으로 끝나는 경우 하드웨어가 다음 바이트의 시작 비트와 해당 비트의 차이를 구별하는 것이 불가능합니다.

Uno에서 위의 출력을 생성하려면 다음 코드를 작성할 수 있습니다.

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

데이터 비트 수

전송 시간 (이전에는 heh)을 절약하기 위해 다른 수의 데이터 비트를 지정할 수있었습니다. AtMega 하드웨어는 5에서 9까지의 데이터 비트 번호를 지원합니다. 데이터 비트가 적을수록 더 적은 정보를 보낼 수 있지만 더 빠를 것입니다.


패리티 비트

선택적으로 패리티 비트를 가질 수 있습니다. 필요한 경우 문자에서 1의 수를 세고이 숫자가 홀수인지 또는 필요에 따라 패리티 비트를 0 또는 1로 설정하여 계산합니다.

예를 들어, 문자 "F"(또는 0x46 또는 0b01000110)의 경우 01000110에 3 개의 문자가 있음을 알 수 있습니다. 따라서 우리는 이미 이상한 패리티를 가지고 있습니다. 따라서 패리티 비트는 다음과 같습니다.

  • 패리티 없음 : 생략
  • 짝수 패리티 : 1 (3 + 1은 짝수)
  • 홀수 패리티 : a 0 (3 + 0이 홀수 임)

패리티 비트 (있는 경우)는 마지막 데이터 비트 뒤에 있지만 중지 비트 앞에 나타납니다.

수신기가 올바른 패리티 비트를 얻지 못하면이를 "패리티 오류"라고합니다. 문제가 있음을 나타냅니다. 발신자와 수신자가 서로 다른 전송 속도 (비트)를 사용하도록 구성되었거나 회선에 0이 1로 바뀌거나 그 반대로 노이즈가있었습니다.

일부 초기 시스템에서는 "mark"패리티 (데이터에 관계없이 패리티 비트가 항상 1 인 경우) 또는 "space"패리티 (데이터에 관계없이 패리티 비트가 항상 0 인 경우)를 사용했습니다.


9 비트 전송

일부 통신 장비는 9 비트 데이터를 사용하므로이 경우 패리티 비트는 9 비트로 바뀝니다. 이 9 번째 비트를 전송하는 특별한 기술이 있습니다 (레지스터는 8 비트 레지스터이므로 9 번째 비트는 다른 곳에 두어야합니다).


정지 비트 수

초기 장비는 전자적으로 다소 느린 경향이 있으므로 수신 바이트를 처리하는 수신기에 시간을주기 위해 송신자가 두 개의 정지 비트를 보내도록 지정하는 경우가있었습니다. 이것은 기본적으로 다음 시작 비트가 나타나기 전에 데이터 라인이 높은 상태 (1 비트 시간 이상)로 유지되는 시간을 더 추가합니다. 이 여분의 비트 시간은 마지막 수신 바이트를 처리하는 수신기 시간을 제공합니다.

정지 비트가되어야 할 때 수신기가 논리 1을 얻지 못하면이를 "프레이밍 오류"라고합니다. 문제가 있음을 나타냅니다. 발신자와 수신자가 서로 다른 전송 속도를 사용하도록 구성되어있을 수 있습니다.


표기법

일반적으로 직렬 통신은 다음과 같이 속도, 데이터 비트 수, 패리티 유형 및 정지 비트 수를 알려줍니다.

9600/8-N-1

이것은 우리에게 말하고 있습니다 :

  • 초당 9600 비트
  • 8 데이터 비트
  • 패리티 없음 (대신 E = even, O = odd)
  • 1 정지 비트

발신자와 수신자가 위의 사항에 동의하는 것이 중요합니다. 그렇지 않으면 통신이 성공하지 않을 수 있습니다.


핀아웃

Arduino Uno에는 하드웨어 직렬에 사용할 수있는 디지털 핀 0과 1이 있습니다.

Arduino Uno 직렬 핀

두 Arduino를 연결하려면 다음 과 같이 Tx와 Rx 를 교체 하십시오.

두 개의 Arduino를 함께 연결


속도

다양한 속도가 지원됩니다 (아래 그림 참조). "표준"속도는 일반적으로 300 보드의 배수입니다 (예 : 300/600/1200/2400 등).

다른 "비표준"속도는 적절한 레지스터를 설정하여 처리 할 수 ​​있습니다. HardwareSerial 클래스가이를 수행합니다. 예.

Serial.begin (115200);  // set speed to 115200 baud

일반적으로 8 비트 데이터를 사용한다고 가정하면 전송 속도를 10으로 나누어 시작 비트와 정지 비트로 인해 초당 전송할 수있는 바이트 수를 추정 할 수 있습니다.

따라서 9600 보드에서 9600 / 10 = 960초당 960 바이트 ( )를 전송할 수 있습니다 .


전송 속도 오류

Atmega의 전송 속도는 시스템 클럭을 나누어 미리 설정된 숫자까지 세어 생성합니다. 데이터 시트의이 표는 16MHz 클록 (예 : Arduino Uno의 클록)에 대한 레지스터 값과 오류 백분율을 보여줍니다.

전송 속도 오류

U2Xn 비트는 클럭 속도 제수에 영향을줍니다 (0 = 16으로 나누기, 1 = 8으로 나누기). UBRRn 레지스터에는 프로세서가 계산하는 숫자가 포함됩니다.

위의 표에서 우리는 다음과 같이 16MHz 클럭에서 9600 보드를 얻습니다.

16000000 / 16 / 104 = 9615

카운터는 0에 상대적이므로 104가 아닌 104로 나눕니다. 따라서 여기의 오류는 15 / 9600 = 0.0016위의 표에 표시된 것과 비슷합니다 (0.02 %).

일부 전송 속도는 다른 전송 속도보다 더 높은 오류를 나타냅니다.

데이터 시트에 따르면 8 개의 데이터 비트에 대한 최대 오류 백분율은 1.5 % ~ 2.0 %입니다 (자세한 내용은 데이터 시트 참조).


아두 이노 레오나르도

Arduino Leonardo와 Micro는 직렬 포트가 아닌 USB를 통해 호스트 컴퓨터에 직접 연결되므로 직렬 통신에 대해 다른 접근 방식이 있습니다.

이 때문에 Serial은 다음과 같이 몇 줄을 추가하여 (소프트웨어가 USB 연결을 설정함에 따라) "준비"될 때까지 기다려야합니다.

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

그러나 실제로 USB 케이블이 아닌 핀 D0 및 D1을 통해 통신하려면 Serial 대신 Serial1을 사용해야합니다. 당신은 이렇게 이렇게 :

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

전압 레벨

Arduino는 직렬 통신에 TTL 레벨을 사용합니다. 이것은 다음을 기대한다는 것을 의미합니다.

  • "0"비트는 0V
  • "1"비트는 + 5V입니다

PC의 직렬 포트에 연결하도록 설계된 구형 직렬 장비는 아마도 다음과 같은 RS232 전압 레벨을 사용합니다.

  • "0"비트는 +3 ~ +15 볼트입니다
  • "1"비트는 -3 ~ -15 볼트입니다

TTL 레벨 ( "1"이 "0"보다 음수)과 관련하여이 "반전"일뿐만 아니라 Arduino는 입력 핀 (5V보다 큰 양수)에서 음의 전압을 처리 할 수 ​​없습니다.

따라서 이러한 장치와 통신하기위한 인터페이스 회로가 필요합니다. (Arduino 로의) 입력의 경우 간단한 트랜지스터, 다이오드 및 두 개의 저항이이를 수행합니다.

인 버팅 버퍼

양방향 통신을 위해서는 네거티브 전압을 생성 할 수 있어야하므로 더 복잡한 회로가 필요합니다. 예를 들어 MAX232 칩은 4 개의 1µF 커패시터와 함께 차지 펌프 회로 역할을한다.


소프트웨어 시리얼

SoftwareSerial이라는 라이브러리가있어 하드웨어가 아닌 소프트웨어에서 직렬 통신 (최대 지점)을 수행 할 수 있습니다. 이것은 직렬 통신에 다른 핀 구성을 사용할 수 있다는 이점이 있습니다. 단점은 소프트웨어에서 직렬을 수행하면 프로세서가 많이 사용되고 오류가 발생하기 쉽다는 것입니다. 자세한 내용은 소프트웨어 직렬 을 참조하십시오.


메가 2560

Arduino "Mega"에는 3 개의 추가 하드웨어 직렬 포트가 있습니다. 보드에는 Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3으로 표시되어 있습니다. 가능한 경우 SoftwareSerial보다 우선적으로 사용해야합니다. 다른 포트를 열려면 다음과 같이 Serial1, Serial2, Serial3이라는 이름을 사용하십시오.

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

인터럽트

HardwareSerial 라이브러리를 사용하여 송수신하는 데 인터럽트가 사용됩니다.

배상

를 수행하면 Serial.print인쇄하려는 데이터가 내부 "전송"버퍼에 저장됩니다. 1024 바이트 이상의 RAM이있는 경우 (예 : Uno) 64 바이트 버퍼가, 그렇지 않으면 16 바이트 버퍼가 나타납니다. 버퍼에 공간이 있으면 Serial.print즉시 반환되므로 코드가 지연되지 않습니다. 공간이 없으면 버퍼가 비워 질 때까지 대기 할 공간을 "차단"합니다.

그런 다음 각 바이트가 하드웨어에 의해 전송 될 때 인터럽트 ( "USART, Data Register Empty"인터럽트)가 호출되고 인터럽트 루틴은 버퍼에서 다음 바이트를 직렬 포트 밖으로 보냅니다.

전수

수신 데이터가 수신되면 인터럽트 루틴 ( "USART Rx Complete"인터럽트)이 호출되고 수신 바이트는 "수신"버퍼 (위에 언급 된 전송 버퍼와 동일한 크기)에 배치됩니다.

호출 Serial.available하면 해당 "수신"버퍼에서 사용 가능한 바이트 수를 알 수 있습니다. 호출 Serial.read하면 바이트가 수신 버퍼에서 제거되어 코드로 반환됩니다.

1000 바이트 이상의 RAM이있는 Arduino의 경우 데이터를 채우지 않으면 수신 버퍼에서 데이터를 제거하기 위해 서두를 필요가 없습니다. 채워지면 더 이상 들어오는 데이터가 삭제됩니다.

이 버퍼의 크기로 인해 다음과 같이 매우 많은 바이트가 도착할 때까지 기다릴 필요가 없습니다.

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

버퍼가 그렇게 많이 보유 할 수 없기 때문에 이것은 작동하지 않습니다.


  • 읽기 전에 항상 데이터를 사용할 수 있는지 확인하십시오. 예를 들어, 이것은 잘못되었습니다 :

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    Serial.available테스트는 당신이 보장 그러나 코드는 두 개의 읽기를 시도합니다, 사용할 수있는 바이트가. 버퍼에 2 바이트가 있으면 작동하지 않을 수 있습니다. 인쇄하지 않으면 'ÿ'처럼 보이는 -1이 반환됩니다.

  • 데이터를 보내는 데 걸리는 시간을 알고 있어야합니다. 위에서 언급했듯이 9600 보드에서는 초당 960 바이트 만 전송하므로 9600 보드에서 아날로그 포트에서 1000 개의 판독 값을 보내려고 시도해도 성공하지 못합니다.


참고 문헌


첫 번째 그래픽 : 화살표가 있으면 정지 비트가 먼저 전송 된 것처럼 보입니다. Rx / Tx와 화살표의 방향을 바꾸면 덜 혼란 스럽다고 생각합니다.
ott--

왼쪽에서 오른쪽으로 읽도록 의도되었으므로 (이 문장 그대로) 왼쪽의 내용이 먼저 발생합니다. 이것을 오실로스코프에 넣으면 트레이스가 표시됩니다.
Nick Gammon

오실로 스코프 설명으로 확인하십시오. :-)
ott--

그러나 나는 당신의 요점이 많은 의미가 있다고 생각했습니다. 다른 사람들은 어떻게 생각합니까? 화살표가 반전되어 Rx / Tx를 교환하면 더 명확합니까?
Nick Gammon

1
@ linhartr22 "무의미한 데이터"를 읽도록 수정했습니다.
Nick Gammon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.