Arduino는 직렬 버퍼 오버플로를 어떻게 처리합니까?


27

Arduino는 직렬 버퍼 오버플로를 어떻게 처리합니까? 최신 수신 데이터 또는 가장 오래된 데이터를 버립니까? 버퍼는 몇 바이트를 유지할 수 있습니까?

serial 

답변:


13

하드웨어 직렬 포트의 경우 HardwareSerial.cpp 에서 버퍼 크기가 특정 AVR에서 사용 가능한 RAM의 양에 따라 달라지는 것을 확인할 수 있습니다 .

#if (RAMEND < 1000)
    #define SERIAL_BUFFER_SIZE 16
#else
    #define SERIAL_BUFFER_SIZE 64
#endif

SoftwareSerial.h 의 소프트웨어 직렬 포트의 경우 수신자 버퍼 크기 _SS_MAX_RX_BUFF는 64 바이트로 정의됩니다. 두 경우 모두 수신 된 데이터가 가득 차면 대기열에 삽입을 시도하지 않으므로 대기열에서 데이터를 검색하는 방법에 따라 이전 데이터와 새 데이터를 혼합 할 수 있습니다.

버퍼 충진을 방지하기 위해 버퍼를 항상 신속하게 비우는 것이 가장 좋습니다. 문제가 메인 루프를 차단하는 다른 코드와 관련이 있다면 타이머를 살펴보고 간단한 상태 머신을 구현하십시오.


Arduino에 데이터를 전송하고 Arduino 측에 활성 "풀러"가 없으면 버퍼에 들어갈 수있는 것보다 많은 데이터가 도착하면 버려 질 것이라는 인상을 얻습니다. 확인할 수 있습니까? 필자는 데이터를 보유 할 수있는 공간이 확보 될 때까지 송신기가 차단한다고 가정했다.
Kolban

방금 모든 코드 (/usr/share/arduino/hardware/arduino/core/arduino/HardwareSer‌ial.cpp)를 살펴보고 여기에 작성한 내용을 확인할 수 있습니다. 내가 추가 할 유일한 것은 SRAM이 2K (RAMEND> 1000)이기 때문에 if 문이 Nano 또는 Uno에서 항상 16이 아닌 64를 사용한다는 것입니다. 따라서 링 버퍼의 크기를 확장하려는 경우이를 변경할 수 있습니다
SDsolar

5

전수

들어오는 바이트가 링 버퍼가 가득 차면 폐기되는 것을 HardwareSerial의 소스에서 볼 수 있습니다.

inline void store_char(unsigned char c, ring_buffer *buffer)
{
  int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
    buffer->buffer[buffer->head] = c;
    buffer->head = i;
  }
}

Arduino에 데이터를 전송하고 Arduino 측에 활성 "풀러"가 없으면 버퍼에 들어갈 수있는 것보다 많은 데이터가 도착하면 삭제됩니다. 당신은 그것을 확인할 수 있습니까?

예, 버려집니다. 직접 구현하지 않으면 소프트웨어 또는 하드웨어 흐름 제어가 없습니다.

그러나 64 바이트 버퍼를 사용하고 9600 보드에서 데이터를 수신하면 1.04ms마다 1 바이트를 얻으므로 버퍼를 채우는 데 66.6ms가 걸립니다. 16MHz 프로세서에서는 채워지지 않을 정도로 버퍼를 자주 확인할 수 있어야합니다. 실제로 처리하지 않으려면 HardwareSerial 버퍼에서 자신의 데이터로 데이터를 이동하기 만하면됩니다.

#if (RAMEND < 1000)1000 바이트 이상의 RAM을 가진 프로세서가 64 바이트 버퍼를 얻는다는 것을 확인할 수 있습니다. RAM은 16 바이트 버퍼를 덜받습니다.


배상

작성하는 데이터는 동일한 크기의 버퍼 (16 또는 64 바이트)에 배치됩니다. 버퍼가 버퍼를 채우면 전송하는 경우 다음 바이트를 직렬 포트로 보내기 위해 인터럽트를 기다리는 "블록"코드를 채 웁니다.

인터럽트가 꺼져 있으면이 문제가 발생하지 않으므로 인터럽트 서비스 루틴 내부에서 직렬 인쇄 를 수행 하지 마십시오.


9600 보드에서 ~ 0.1ms마다 바이트를 얻으므로 버퍼를 채우는 데 6.6ms 밖에 걸리지 않습니다.
Eric Dand

1
9600 보드에서 초당 9600 비트 를 얻습니다 . 각 바이트는 10 비트 (8 데이터 + 1 시작 비트 + 1 정지 비트)이므로 초당 960 바이트를 얻습니다. 1/960 = 0.001042 s-1.04ms마다 1 바이트입니다.
Nick Gammon

Ahh는 물론 바이트가 아닙니다! 수정 해 주셔서 감사합니다.
Eric Dand

Nick, 나를 위해 대답하십시오 : 데이터 로거로 입력을 기다리는 ser.readline ()에 Python을 실행하는 Pi가 있고 Arduino가 직렬을 통해 판독 한 다음 탭을 사용하여 배치로 전송하면 delimeters, delay (120000)를 사용하여 배치가 2 분마다 나오면 파이썬 내장 문자는 개행 문자가 나타날 때까지 각 문자를 즉시 ​​읽습니다.이 시점에서 전체 줄을 반환 값으로 해제합니다. 따라서 총 80자를 보내도 Arduino 버퍼 크기에 대해 걱정할 필요가 없습니다. 이것이 좋은 가정일까요?
SDsolar

예,이 시나리오에서는 전송 버퍼 크기가 중요하지 않습니다. 작은 버퍼는 (약간) 전송 속도를 늦출 수 있지만, 시간이 오래 걸리면 신경 쓰지 않아도됩니다.
Nick Gammon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.