micropython — MicroPython 내부 접근 및 제어

함수

micropython.const(expr: int) int

컴파일러가 표현식을 최적화할 수 있도록 해당 표현식이 상수임을 선언하는 데 사용됩니다. 이 함수는 다음과 같이 사용해야 합니다:

from micropython import const

CONST_X = const(123)
CONST_Y = const(2 * CONST_X + 1)

이런 방식으로 선언된 상수는 선언된 모듈 외부에서도 여전히 전역 변수로 접근할 수 있습니다. 반면, 상수가 밑줄로 시작하면 숨겨져서 전역 변수로 사용할 수 없으며 실행 중에 메모리를 전혀 차지하지 않습니다.

const 함수는 MicroPython 파서가 직접 인식하며, 주로 위 패턴을 따름으로써 CPython과 MicroPython 모두에서 실행되는 스크립트를 작성할 수 있도록 micropython 모듈의 일부로 제공됩니다.

micropython.opt_level(level: int | None = None) int | None

level이 주어지면 이 함수는 이후 스크립트 컴파일에 대한 최적화 수준을 설정하고 None을 반환합니다. 그렇지 않으면 현재 최적화 수준을 반환합니다.

최적화 수준은 다음과 같은 컴파일 기능을 제어합니다:

  • 어설션(Assertions): 수준 0에서는 어설션 문이 활성화되어 바이트코드로 컴파일됩니다. 수준 1 이상에서는 어설션이 컴파일되지 않습니다.

  • 내장 __debug__ 변수: 수준 0에서는 이 변수가 True로 확장됩니다. 수준 1 이상에서는 False로 확장됩니다.

  • 소스 코드 줄 번호: 수준 0, 1, 2에서는 예외가 발생한 줄 번호를 보고할 수 있도록 소스 코드 줄 번호가 바이트코드와 함께 저장됩니다. 수준 3 이상에서는 줄 번호가 저장되지 않습니다.

기본 최적화 수준은 일반적으로 수준 0입니다.

micropython.alloc_emergency_exception_buf(size: int) None

긴급 예외 버퍼를 위해 size 바이트의 RAM을 할당합니다(약 100바이트가 적당한 크기입니다). 이 버퍼는 일반 RAM 할당이 실패할 수 있는 경우(예: 인터럽트 핸들러 내부)에 예외를 생성하는 데 사용되며, 따라서 이러한 상황에서 유용한 트레이스백 정보를 제공합니다.

이 함수를 사용하는 좋은 방법은 메인 스크립트의 시작 부분(예: boot.py 또는 main.py)에 두는 것입니다. 그러면 그 뒤에 오는 모든 코드에 대해 긴급 예외 버퍼가 활성화됩니다.

micropython.mem_info(verbose: Any | None = None) None

현재 사용 중인 메모리에 관한 정보를 출력합니다. verbose 인수가 주어지면 추가 정보가 출력됩니다.

출력되는 정보는 구현에 따라 다르지만, 현재는 사용된 스택과 힙의 양을 포함합니다. verbose 모드에서는 전체 힙을 출력하여 어떤 블록이 사용 중이고 어떤 블록이 비어 있는지 표시합니다.

micropython.qstr_info(verbose: Any | None = None) None

현재 인턴된(interned) 문자열에 관한 정보를 출력합니다. verbose 인수가 주어지면 추가 정보가 출력됩니다.

출력되는 정보는 구현에 따라 다르지만, 현재는 인턴된 문자열의 개수와 그것들이 사용하는 RAM의 양을 포함합니다. verbose 모드에서는 RAM에 인턴된 모든 문자열의 이름을 출력합니다.

micropython.stack_use() int

현재 사용 중인 스택의 양을 나타내는 정수를 반환합니다. 이 값의 절대값은 그다지 유용하지 않으며, 오히려 서로 다른 지점에서의 스택 사용량 차이를 계산하는 데 사용해야 합니다.

micropython.heap_lock() None

힙을 잠급니다. 잠긴 동안에는 메모리 할당이 발생할 수 없으며, 힙 할당이 시도되면 MemoryError가 발생합니다.

잠금은 중첩됩니다. heap_lock()을 여러 번 호출하면 잠금 깊이가 증가합니다. 힙은 heap_unlock()이 동일한 횟수만큼 호출될 때까지 잠긴 상태로 유지됩니다.

힙이 잠긴 상태에서 REPL이 활성화되면 강제로 잠금이 해제됩니다.

micropython.heap_unlock() int

힙 잠금 깊이를 1만큼 감소시키고 새 깊이를 음수가 아닌 정수로 반환합니다. 반환 값이 0이면 힙이 더 이상 잠겨 있지 않으며 할당이 다시 허용됨을 의미합니다.

micropython.heap_locked() int

현재 힙 잠금 깊이를 음수가 아닌 정수로 반환합니다. 0은 힙이 잠겨 있지 않음을 의미합니다.

참고: 이 함수는 OpenMV Cam에서 사용할 수 없습니다.

micropython.kbd_intr(chr: int) None

KeyboardInterrupt 예외를 발생시킬 문자를 설정합니다. 기본적으로 스크립트 실행 중에는 Ctrl-C에 해당하는 3으로 설정됩니다. 이 함수에 -1을 전달하면 Ctrl-C 캡처가 비활성화되고, 3을 전달하면 복원됩니다.

이 함수는 일반적으로 REPL에 사용되는 들어오는 문자 스트림에서 Ctrl-C 캡처를 방지하는 데 사용할 수 있으며, 이는 해당 스트림이 다른 목적으로 사용되는 경우에 유용합니다.

micropython.schedule(func: Callable[[Any], Any], arg: Any) None

함수 func를 “매우 곧” 실행하도록 예약합니다. 이 함수에는 단일 인수로 arg 값이 전달됩니다. “매우 곧”이란, MicroPython 런타임이 효율성을 유지하려고 노력하면서도 다음 조건이 충족되는 한 가능한 가장 빠른 시점에 함수를 실행하기 위해 최선을 다한다는 것을 의미합니다:

  • 예약된 함수는 다른 예약된 함수를 결코 선점하지 않습니다.

  • 예약된 함수는 항상 “opcode 사이에서” 실행되며, 이는 모든 기본적인 Python 연산(예: 리스트에 추가)이 원자적임이 보장된다는 것을 의미합니다.

  • 특정 포트는 예약된 함수가 결코 실행되지 않는 “임계 영역(critical regions)”을 정의할 수 있습니다. 임계 영역 내에서 함수를 예약할 수는 있지만, 해당 영역을 벗어날 때까지 실행되지 않습니다. 임계 영역의 예로는 선점형 인터럽트 핸들러(IRQ)가 있습니다.

  • 네이티브 코드 함수 내부에서는, 네이티브 코드가 명시적으로 그렇게 하는 함수를 호출하지 않는 한 예약된 함수가 호출되지 않습니다.

  • poll.poll, poll.ipoll, time.sleep, time.sleep_ms(지속 시간이 0인 sleep 포함)를 비롯한 특정 함수는 스케줄된 함수를 호출합니다.

이 함수의 용도 중 하나는 선점형 IRQ에서 콜백을 예약하는 것입니다. 이러한 IRQ는 IRQ 내에서 실행되는 코드에 제약을 부과하며(예: 힙이 잠길 수 있음), 나중에 호출할 함수를 예약하면 이러한 제약이 해제됩니다.

멀티스레드 포트에서 예약된 함수의 동작은 특정 포트에 대해 전역 인터프리터 잠금(GIL)이 활성화되어 있는지 여부에 따라 달라집니다:

  • GIL이 활성화된 경우, 함수는 어떤 스레드든 선점하여 그 컨텍스트에서 실행될 수 있습니다.

  • GIL이 비활성화된 경우, 함수는 메인 스레드만 선점하여 그 컨텍스트에서 실행됩니다.

참고: schedule()이 선점형 IRQ에서 호출되고, 메모리 할당이 허용되지 않으며, schedule()에 전달할 콜백이 바운드 메서드(bound method)인 경우, 이를 직접 전달하면 실패합니다. 이는 바운드 메서드에 대한 참조를 생성하면 메모리 할당이 발생하기 때문입니다. 해결책은 클래스 생성자에서 메서드에 대한 참조를 생성하고 그 참조를 schedule()에 전달하는 것입니다. 이는 “Creation of Python objects” 아래의 참조 문서에서 자세히 다룹니다.

예약된 함수를 담을 큐는 크기가 유한하며, 큐가 가득 차면 schedule()RuntimeError를 발생시킵니다.

클래스

class micropython.RingIO(size: int)
class micropython.RingIO(buffer: bytes | bytearray | memoryview)

스트림 인터페이스를 갖춘 고정 크기의 바이트 링버퍼를 제공합니다. io.BytesIO의 FIFO 큐 변형으로 볼 수 있습니다. 두 가지 생성자 형식은 백킹 버퍼가 공급되는 방식에서만 차이가 있습니다:

  • RingIO(size)는 백킹 버퍼를 내부적으로 할당합니다. 고전적인 링버퍼 알고리즘은 추적을 위해 1바이트를 예약하므로, 할당되는 버퍼는 size보다 1바이트 더 크며 인스턴스는 전체 size 바이트의 데이터를 담을 수 있습니다. 예를 들어, RingIO(16)은 17바이트 버퍼를 할당하고 16바이트의 데이터를 담습니다.

  • RingIO(buffer)는 버퍼를 할당하는 대신 공급된 buffer를 그대로 사용합니다. 추적을 위해 1바이트가 예약되므로, 인스턴스는 len(buffer) - 1 바이트의 데이터를 담을 수 있습니다. 예를 들어, RingIO(bytearray(16))은 15바이트의 데이터를 담습니다.

RingIO 인스턴스는 단일 방향으로 데이터를 전달하는 데 사용될 때(예: IRQ에서 쓰고 비IRQ 함수에서 읽거나 그 반대) IRQ/스레드 안전합니다. 단일 인스턴스에 IRQ와 비IRQ 컨텍스트 양쪽에서 쓰는 경우에는 이것이 성립하지 않으며, 종종 데이터 손상을 일으킵니다.

any() int

읽을 수 있는 문자 수를 세는 정수를 반환합니다.

read(nbytes: int | None = None) bytes

사용 가능한 문자를 읽습니다. 이것은 논블로킹 함수입니다. nbytes가 지정되면 최대 그만큼의 바이트를 읽고, 그렇지 않으면 가능한 한 많은 데이터를 읽습니다.

반환 값: 읽은 바이트를 담은 bytes 객체입니다. 사용 가능한 데이터가 없으면 길이가 0인 bytes 객체가 됩니다.

readline(nbytes: int | None = None) bytes

버퍼에 줄바꿈 문자가 있으면 그 문자나 캐리지 리턴으로 끝나는 한 줄을 읽고, 없으면 버퍼에서 사용 가능한 바이트를 반환합니다. nbytes가 지정되면 최대 그만큼의 바이트를 읽습니다.

반환 값: 읽은 줄을 담은 bytes 객체입니다.

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

사용 가능한 바이트를 제공된 buf로 읽어들입니다. nbytes가 지정되면 최대 그만큼의 바이트를 읽습니다. 그렇지 않으면 최대 len(buf) 바이트를 읽습니다.

반환 값: buf로 읽어들인 바이트 수의 정수 카운트입니다.

write(buf: bytes | bytearray | memoryview) int

링버퍼의 사용 가능한 공간에 의해 제한되어, buf의 바이트를 링버퍼로 논블로킹 쓰기를 수행합니다.

반환 값: 쓰여진 바이트 수의 정수 카운트입니다.

close() None

표준 stream 인터페이스의 일부로 제공되는 no-op입니다. 링버퍼의 데이터에는 아무런 영향을 미치지 않습니다.