스케치가 왜 공간과 메모리를 많이 차지합니까?


12

Yún에 대한이 스케치를 컴파일 할 때 :

int led = 7;

void setup() {                
  pinMode(led, OUTPUT);     
}

void loop() {
  digitalWrite(led, HIGH);
}

나는 얻다:

Sketch는 5,098 바이트 (17 %)의 프로그램 저장 공간을 사용합니다.

최대 값은 28,672 바이트입니다. 전역 변수는 153 바이트 (5 %)의 동적 메모리를 사용하므로 지역 변수에는 2,407 바이트가 남습니다. 최대 값은 2,560 바이트입니다.

BareMinimum 스케치를 컴파일 할 때도 :

void setup() {                
  // setup    
}

void loop() {
  // loop
}

나는 얻다:

Sketch는 4,548 바이트 (15 %)의 프로그램 저장 공간을 사용합니다.

최대 값은 28,672 바이트입니다. 전역 변수는 151 바이트 (5 %)의 동적 메모리를 사용하므로 지역 변수에는 2,409 바이트가 남습니다. 최대 값은 2,560 바이트입니다.

최소 스케치가 할당 된 프로그램 저장 공간의 15 %를 차지하는 이유는 무엇입니까? 그리고 왜 매우 간단한 스케치가 프로그램 저장 공간의 17 %를 차지합니까? Arduino 웹 사이트 에 따르면 :

프로그램에 많은 문자열을 사용하여 쉽게 사용할 수 있습니다. 예를 들어, 다음과 같은 선언 char message[] = "I support the Cape Wind project.";은 33 바이트를 SRAM에 넣습니다 (각 문자는 바이트와 '\ 0'종결자를 사용함).

그러나 이러한 스케치 중 하나에 선언 된 문자열이 없습니다.

마치 내가 지정하지 않은 다른 라이브러리 / 클래스를 가져 오거나 사용할 수있는 것처럼 보입니다. 시스템 기본 라이브러리를 가져 왔을까요? 아니면 다른 것입니까?

답변:


6

윤은 콤보입니다. Part Arduino 및 Part OpenWRT (Linux). 귀하의 질문은 Arduino와 관련이 있습니다. 실제로 이것은 UNO (ATmega328p)가 아닌 Leonardo와 비슷한 ATmega32u4입니다. 32u4 (Leo) 는 UNO에 실제 직렬 포트 (일명 UART)가있는 USB를 통해 가상 직렬 포트를 통해 통신합니다 (짧은 답변 : 지원해야 함). 다음은 AVR 프로세서에 대한 다양한 보드 유형의 빌드 통계입니다.

UNO에는 USB를 직렬 포트의 DTR 핀으로 변환하는 외부 칩이 있는데,이 칩은 연결될 때 ATmega328의 리셋 핀을 토글하여 부트 로더로 재부팅합니다. 반대로 Leo / Yun의 USB- 직렬은 32u4의 펌웨어에 구현되어 있습니다. 따라서 Leo 또는 YUN의 32u4 칩을 원격으로 재부팅하려면로드 된 펌웨어가 항상 USB 클라이언트 측 드라이버를 지원해야합니다. 약 4K를 소비합니다.

USB가 필요하지 않고 UNO에서 BareMinimum.ino의 경우처럼 다른 라이브러리 리소스가 호출되지 않은 경우 코어 Arduino 라이브러리에는 약 466 바이트 만 필요합니다.

UNO (ATmega328p)에서 BareMinimum.ino의 통계 컴파일

Sketch uses 466 bytes (1%) of program storage space. Maximum is 32,256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

Leonardo (ATmega32u4)에서 BareMinimum.ino의 통계 컴파일

Sketch uses 4,554 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.

Yun (ATmega32u4)에서 BareMinimum.ino의 통계 편집

Sketch uses 4,548 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 151 bytes (5%) of dynamic memory, leaving 2,409 bytes for local variables. Maximum is 2,560 bytes.

7

Arduino는 많은 표준 라이브러리, 인터럽트 등에서 컴파일합니다. 예를 들어 pinMode 및 digitalWrite 함수는 조회 테이블을 사용하여 GPIO가 데이터를 쓰기 위해 등록하는 런타임을 알아냅니다. 또 다른 예는 Arduino가 시간을 추적하고 기본적으로 일부 인터럽트를 정의 하며이 모든 기능에는 약간의 공간이 필요하다는 것입니다. 프로그램을 확장하면 풋 프린트가 약간만 바뀐다는 것을 알 수 있습니다.

개인적으로 "부풀림"없이 컨트롤러를 최소한으로 프로그래밍하고 싶지만 사용하기 쉬운 여러 기능이 더 이상 사용할 수 없기 때문에 EE.SE와 SO의 세계로 빠르게 들어갈 수 있습니다. 더 작은 풋 프린트로 컴파일되는 pinMode 및 digitalWrite에 대한 대체 라이브러리가 있지만 정적 컴파일 된 핀 ( led변수는 없지만 상수 임) 과 같은 다른 단점이 있습니다.


기본적으로 그것은 당신이 묻지 않고 모든 종류의 표준 라이브러리에서 컴파일합니까? 산뜻한.
hichris123

그렇습니다, 나는 보통 그것을 "부푼"이라고 부릅니다. 그러나 그것은 실제로 유용성입니다. Arduino는 엔트리 레벨이 낮은 환경으로 너무 많은 생각없이 작동합니다. 더 필요한 경우 Arduino를 사용하면 대체 라이브러리를 사용하거나 베어 메탈에 대해 컴파일 할 수 있습니다. 마지막은 아마 Arduino.SE의 범위를 벗어난 것입니다.
jippie

내 @mpflaga 답변을 참조하십시오. 팽창이 많지 않습니다. 또는 최소한 최소한의 기능을 위해 핵심 라이브러리에 있습니다. 스케치를 호출하지 않는 한 표준 라이브러리가 많이 포함되어 있지 않습니다. 오히려 15 %는 32u4의 USB 지원 때문입니다.
mpflaga

4

이미 완벽하게 좋은 답변이 있습니다. 나는 언젠가 나에게 같은 종류의 질문을 한 통계를 공유하기 위해 이것을 게시하고있다. 최소한의 스케치에서 너무 많은 공간을 차지하는 것은 무엇입니까? 동일한 기능을 달성하기 위해 필요한 최소량은 얼마입니까?

아래는 매번 핀 13의 LED를 토글하는 최소한의 깜박임 프로그램의 세 가지 버전입니다. 세 가지 버전 모두 avr-gcc 4.8.2, avr-libc 1.8.0 및 arduino-core 1.0.5 (Arduino IDE를 사용하지 않음)를 사용하여 Uno (USB 포함 안 됨) 용으로 컴파일되었습니다.

먼저 표준 Arduino 방식 :

const uint8_t ledPin = 13;

void setup() {
    pinMode(ledPin, OUTPUT);
}

void loop() {
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(ledPin, LOW);
    delay(1000);
}

1018 바이트로 컴파일됩니다. 둘 다 avr-nm와 분해를 사용하여 해당 크기를 개별 기능으로 분류했습니다. 가장 큰 것에서 가장 작은 것까지 :

 148 A ISR(TIMER0_OVF_vect)
 118 A init
 114 A pinMode
 108 A digitalWrite
 104 C vector table
  82 A turnOffPWM
  76 A delay
  70 A micros
  40 U loop
  26 A main
  20 A digital_pin_to_timer_PGM
  20 A digital_pin_to_port_PGM
  20 A digital_pin_to_bit_mask_PGM
  16 C __do_clear_bss
  12 C __init
  10 A port_to_output_PGM
  10 A port_to_mode_PGM
   8 U setup
   8 C .init9 (call main, jmp exit)
   4 C __bad_interrupt
   4 C _exit
-----------------------------------
1018   TOTAL

위의 목록에서 첫 번째 열은 바이트 단위의 크기이며 두 번째 열은 코드가 Arduino 코어 라이브러리 (총 822 바이트), C 런타임 (C, 148 바이트) 또는 사용자 (U , 48 바이트).

이 목록에서 볼 수 있듯이 가장 큰 기능은 타이머 0 오버플로 인터럽트를 처리하는 루틴입니다. 이 루틴은 시간을 추적 할 책임이 있으며, 필요로한다 millis(), micros()하고 delay(). 두 번째로 큰 기능은 init()PWM에 대한 하드웨어 타이머를 설정하고 TIMER0_OVF 인터럽트를 활성화하고 USART (부트 로더가 사용한)를 분리합니다. 이 함수와 이전 함수는 모두에 정의되어 <Arduino directory>/hardware/arduino/cores/arduino/wiring.c있습니다.

다음은 C + avr-libc 버전입니다.

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB |= _BV(PB5);     /* set pin PB5 as output */
    for (;;) {
        PINB = _BV(PB5);  /* toggle PB5 */
        _delay_ms(1000);
    }
}

개별 크기의 분류 :

104 C vector table
 26 U main
 12 C __init
  8 C .init9 (call main, jmp exit)
  4 C __bad_interrupt
  4 C _exit
----------------------------------
158   TOTAL

C 런타임의 경우 132 바이트이고 인라인 된 함수를 포함하여 26 바이트의 사용자 코드 _delay_ms()입니다.

이 프로그램은 인터럽트를 사용하지 않기 때문에 인터럽트 벡터 테이블이 필요하지 않으며 일반 사용자 코드를 대신 사용할 수 있습니다. 다음 어셈블리 버전은이를 정확하게 수행합니다.

#include <avr/io.h>
#define io(reg) _SFR_IO_ADDR(reg)

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    ldi r26, 49      ; delay for 49 * 2^16 * 5 cycles
delay:
    sbiw r24, 1
    sbci r26, 0
    brne delay
    rjmp loop

이것은 avr-gcc -nostdlib14 바이트로만 (와 함께 ) 조립 되며, 대부분은 깜박임을 볼 수 있도록 토글을 지연시키는 데 사용됩니다. 해당 지연 루프를 제거하면 너무 빨리 깜박이는 6 바이트 프로그램 (2MHz)으로 끝납니다.

    sbi io(DDRB), 5  ; set PB5 as output
loop:
    sbi io(PINB), 5  ; toggle PB5
    rjmp loop

3

왜 하나의 LED를 깜박이 는데 1000 바이트가 걸리는가 에 대한 게시물을 썼습니다 . .

간단한 대답은 " 두 개의 LED 를 깜박이는 데 2000 바이트가 필요하지 않습니다 !"입니다.

더 긴 대답은 표준 Arduino 라이브러리 (원하지 않는 경우 사용할 필요가 없음)는 삶을 단순화하는 훌륭한 기능을 가지고 있다는 것입니다. 예를 들어, 라이브러리가 핀 8을 올바른 포트 및 올바른 비트 번호로 변환하는 런타임에 핀을 주소로 지정할 수 있습니다. 포트 액세스를 하드 코딩하면 해당 오버 헤드를 줄일 수 있습니다.

사용하지 않더라도 표준 라이브러리에는 "틱"을 계산하는 코드가 포함되어 있으므로 현재 "시간"(을 호출하여 millis())을 찾을 수 있습니다 . 이렇게하려면 일부 인터럽트 서비스 루틴의 오버 헤드를 추가해야합니다.

Arduino Uno에서이 스케치를 단순화하면 프로그램 메모리 사용량이 178 바이트 (IDE 1.0.6)로 낮아집니다.

int main ()
  {
  DDRB = bit (5);
  while (true)
    PINB = bit (5);
  }

178 바이트는 그다지 많지 않으며 첫 104 바이트는 하드웨어 인터럽트 벡터입니다 (26 벡터에 대해 각각 4 바이트).

따라서 LED를 깜박이는 데 필요한 바이트 수는 74 바이트입니다. 그리고 그 74 바이트 중 대부분은 실제로 글로벌 메모리를 초기화하기 위해 컴파일러가 생성 한 코드입니다. 두 개의 LED를 깜박이는 데 충분한 코드를 추가하는 경우 :

int main ()
  {
  DDRB = bit (5);  // pin 13
  DDRB |= bit (4);  // pin 12

  while (true)
    {
    PINB = bit (5); // pin 13
    PINB = bit (4); // pin 12
    }
  }

그런 다음 코드 크기가 186 바이트로 증가합니다. 따라서 186 - 178 = 8LED를 깜박이려면 바이트 만 있으면된다고 주장 할 수 있습니다.

따라서 8 바이트는 LED를 깜박입니다. 나에게는 매우 효율적으로 들린다.


집에서 이것을 시도하고 싶다면 위의 게시 된 코드가 두 개의 LED를 깜박이는 동안 실제로 매우 빠르게 수행된다는 것을 지적해야합니다. 실제로 2MHz에서 깜박입니다. 스크린 샷을 참조하십시오. 채널 1 (노란색)은 핀 12, 채널 2 (청록색)는 핀 13입니다.

핀 12 및 13의 빠른 깜박임

보시다시피, 출력 핀에는 주파수가 2 MHz 인 구형파가 있습니다. 핀 13은 코드에서 핀의 토글 순서로 인해 핀 12 이전의 상태 62.5ns (1 클럭 사이클)를 변경합니다.

따라서 내 눈이 훨씬 나아지지 않으면 실제로 깜박임 효과가 나타나지 않습니다.


재미있는 추가 기능으로, 실제로 는 하나의 핀을 토글 하는 것과 같은 양의 프로그램 공간 에서 두 개의 핀을 토글 할 수 있습니다 .

int main ()
  {
  DDRB = bit (4) | bit (5);  // set pins 12 and 13 to output

  while (true)
    PINB =  bit (4) | bit (5); // toggle pins 12 and 13
  } // end of main

178 바이트로 컴파일됩니다.

이것은 당신에게 더 높은 주파수를 제공합니다 :

핀 12 및 13의 매우 빠른 깜박임

이제 최대 2.66MHz입니다.


이것은 의미가 있습니다. 그렇다면 표준 라이브러리는 빌드시 자동으로 헤더 만 포함됩니까? 그리고 당신은 어떻게 할 수 있었다 없습니다 그들을 포함되어 있습니까?
hichris123

2
링커는 사용되지 않는 코드를 적극적으로 제거합니다. 호출하지 않음으로써 init()(통상과 같이 main()수행) 한 후 (갖는 파일 wiring.c init그것에는)에 연결되지 않았다. 따라서, 인터럽트 핸들러의 처리가 (용 millis(), micros()등)을 생략 하였다. 시간을 낼 필요가없는 한, 생략하지 않는 것이 실용적이지는 않지만 실제로는 스케치에 넣은 내용에 따라 스케치의 크기가 커집니다. 예를 들어, Serial을 사용하는 경우 프로그램 메모리와 RAM이 적중합니다.
Nick Gammon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.