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


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.