1. pinctrl은 주변장치의 신호 경로를 맡는다
UART의 TX 신호를 P0.06으로 내보낼지, RX 핀에 내부 pull-up을 걸지 같은 설정은 pin controller의 일이다. 그 하드웨어는 STM32처럼 공통 핀 설정 블록에 모여 있을 수도, Nordic nRF처럼 주변장치의 PSEL 레지스터와 핀 설정 블록에 나뉘어 있을 수도 있다. Zephyr 드라이버는 어느 경우든 하나의 pinctrl 상태를 적용한다. 반면 LED 켜기나 버튼 수준 읽기처럼 핀을 범용 입출력으로 다루는 일은 GPIO API의 영역이다.
↔ 각 상자는 빌드 또는 실행 단계입니다. 좁은 화면에서는 좌우로 이동하세요.
pinctrl-0·pinctrl-names로 상태와 핀 그룹을 연결어떤 신호가 어느 핀으로 가는지 선언PINCTRL_DT_DEFINE가 상태별 pin 배열과 pinctrl_dev_config를 구성빌드 시 결정된 데이터pinctrl_apply_state를 init·PM 등의 코드 경로에서 호출언제 바꿀지 결정pinctrl_configure_pins가 벤더별 하드웨어에 적용mux·pull·저전력 설정이 효력을 얻음| 층 | 답하는 질문 | UART0 사례 |
|---|---|---|
| 보드 + pinctrl | 신호가 어느 핀과 전기 설정을 쓰나? | TX=P0.06, RX=P0.08; RX 쪽 pull-up |
| UART 드라이버 | 장치가 언제 그 상태를 요청하나? | 초기화에서 default 적용 |
| GPIO 드라이버 | 범용 핀 수준을 어떻게 읽고 쓰나? | UART 전용 TX 전환과 같은 계약이 아님 |
2. nRF52840 DK에서 UART0의 두 상태를 읽는다
실제 nrf52840dk_nrf52840.dts는 &uart0에 pinctrl-0 = <&uart0_default>, pinctrl-1 = <&uart0_sleep>을 순서대로 붙이고 pinctrl-names = "default", "sleep"으로 이름을 대응시킨다. 두 label의 내용은 같은 보드의 -pinctrl.dtsi에 있다. default는 UART 신호를 핀에 배치하고 RX·CTS 그룹에 pull-up을 준다. sleep은 같은 네 신호에 low-power-enable을 준다.
각 상태는 완전한 핀 설정 묶음입니다. 한 상태에 다른 상태의 값이 순서대로 더해지는 모델이 아닙니다.
defaultP0.06RXP0.08RTSP0.05CTSP0.07bias-pull-up. UART 드라이버가 운영 상태를 요청할 때 적용.sleepP0.06RXP0.08RTSP0.05CTSP0.07low-power-enable. 선언만으로 진입하는 것은 아님.| UART0 신호 | 보드 핀 | default 그룹 | sleep 그룹 |
|---|---|---|---|
| TX | P0.06 | group1 | group1 · 저전력 |
| RX | P0.08 | group2 · pull-up | group1 · 저전력 |
| RTS | P0.05 | group1 | group1 · 저전력 |
| CTS | P0.07 | group2 · pull-up | group1 · 저전력 |
이 예시의 NRF_PSEL(UART_TX, 0, 6) 같은 숫자와 속성은 Nordic 바인딩의 언어다. 다른 SoC가 동일한 psels 속성을 쓴다는 뜻은 아니다. 보드 배선과 pull-up은 회로·속도에 따라 달라지므로 다른 보드에 이 설정을 그대로 복사하지 않는다.
3. Devicetree label은 드라이버가 쓸 C 설정으로 바뀐다
PINCTRL_DT_DEFINE(node_id)은 노드의 pinctrl-N 항목에서 각 상태의 pin 배열과 장치용 pinctrl_dev_config를 만든다. PINCTRL_DT_DEV_CONFIG_GET(node_id)은 그 설정의 주소를 가져온다. 같은 동작을 compatible 인스턴스 번호로 쓰는 짝이 PINCTRL_DT_INST_DEFINE(inst)과 PINCTRL_DT_INST_DEV_CONFIG_GET(inst)다. 둘은 각각 선택 방식이 다른 API이지 한 인스턴스에 둘 다 선언해야 하는 절차가 아니다.
Nordic UART 드라이버의 인스턴스 매크로는 노드 label을 사용해 PINCTRL_DT_DEFINE(UARTE(idx))를 호출하고, 정적 config의 .pcfg에 PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx))을 둔다. 초기화 함수는 그 포인터로 default 상태를 적용한다. 핀 컨트롤러 구현 쪽에서는 pinctrl_soc_pin_t와 pinctrl_configure_pins()가 SoC별 인코딩과 하드웨어 쓰기를 담당한다.
| 기호 | 입력 | 결과·호출 시점 |
|---|---|---|
PINCTRL_DT_DEFINE | 노드 식별자 | 상태 배열과 device config를 정의 · 컴파일 |
PINCTRL_DT_DEV_CONFIG_GET | 같은 노드 식별자 | 정의된 config의 주소를 드라이버에 연결 · 컴파일 |
pinctrl_apply_state | config 포인터 + 상태 ID | 해당 상태를 찾아 적용 · 실행 |
pinctrl_configure_pins | SoC별 pin 배열 + 선택적 장치 주소 | 핀 설정을 즉시 하드웨어에 반영 · 실행 |
상태가 있는데 배열에서 사라진다면. 3.7.0은 CONFIG_PM과 CONFIG_PM_DEVICE가 모두 꺼진 빌드에서 PINCTRL_SKIP_SLEEP을 정의한다. 그때 Devicetree의 sleep 선언이 있어도 pinctrl 매크로가 이 상태의 C 데이터를 생략할 수 있다. 반대로 PM 옵션을 켰다고 해서 모든 장치가 sleep 상태를 적용하는 것도 아니다.
CONFIG_PINCTRL_DYNAMIC은 default·sleep 사이를 바꾸는 일반 호출과 다르다. 이것은 config가 가리키는 상태 집합 자체를 pinctrl_update_states()로 교체하는 기능이다. v3.7.0 가이드는 이미 동작 중인 장치의 핀 설정을 바꾸면 예기치 않은 결과가 날 수 있으므로, 장치 초기화 이전의 이른 부팅 단계에서만 사용하라고 제한한다.
근거: v3.7.0 상태 생략 규칙, 동적 pin control의 초기화 전 제한, v3.7.0 pinctrl API, UART 매크로와 .pcfg.
4. sleep 선언은 자동 상태 전이가 아니다
v3.7.0 uart_nrfx_uarte.c의 uarte_instance_init()는 PINCTRL_STATE_DEFAULT 적용 실패를 반환한 뒤 UART를 설정·활성화한다. 저전력 경로에서는 pins_state_change(dev, on)가 UARTE_CFG_FLAG_GPIO_MGMT가 있을 때에만 default 또는 sleep을 요청한다. 이 helper는 선택된 비동기 저전력 동작과 device PM의 suspend/resume 경로에서 호출된다. 특히 PM action은 TX/RX를 정리한 다음 핀을 바꾸므로, 핀 상태 변경과 UART 주변장치 정지를 하나의 마법 같은 자동 이벤트로 묶어 생각하면 안 된다.
아래는 조건을 표시한 코드 경로도입니다. 모든 앱·모든 드라이버에서 네 칸을 반드시 지난다는 뜻은 아닙니다.
uarte_instance_initdefault 적용, 실패면 초기화 반환.
단순 DTS 선언이 상태 전이 스케줄러는 아님.
pins_state_change(false). GPIO 관리 플래그가 있을 때 sleep 적용.
pins_state_change(true) 뒤 UART 재활성화. 같은 플래그에서 default 복귀.
| 확인한 사실 | 입증하는 것 | 아직 입증하지 못하는 것 |
|---|---|---|
DTS의 pinctrl-1 | sleep용 핀 그룹 연결 | 실행 중 suspend 호출 여부 |
CONFIG_PM_DEVICE=y | device PM 코드 경로를 빌드할 조건 | 모든 드라이버의 sleep 적용 |
UART init의 default 적용 성공 | 초기 핀 설정 함수의 성공 반환 | 배선·전압·상대 장치 통신 성공 |
pinctrl_apply_state(..., SLEEP) 반환 0 | 드라이버가 이 상태를 찾아 적용 | 보드 전체 전력 절감량 |
5. 핀 문제는 세 산출물을 나란히 보며 좁힌다
보드가 부팅하지만 UART가 조용하면 먼저 build/zephyr/zephyr.dts의 최종 &uart0와 pinctrl label을 본다. 다음에는 build/zephyr/.config에서 UART·pinctrl·PM 선택을 확인한다. 마지막으로 드라이버 초기화 반환값과 실제 TX/RX 핀을 계측한다. source DTS 한 파일만 보고 overlay가 최종 상태를 바꾸지 않았다고 가정하지 않는다.
| 증상 | 먼저 확인 | 잘못된 단정 피하기 |
|---|---|---|
| 컴파일 중 pinctrl 상태/label 오류 | merged DTS의 pinctrl-N, 이름 순서와 phandle | 핀 컨트롤러 레지스터 문제로 시작하지 않기 |
| 초기화가 음수 반환 | pinctrl_apply_state 반환과 SoC pinctrl 구현 | UART baud 설정 실패와 섞지 않기 |
| TX는 되나 RX가 불안정 | 실제 핀 배선, RX 그룹의 pull-up, 상대 장치 전기 조건 | 보드 예시를 다른 회로에 그대로 복사하지 않기 |
| sleep 선언했는데 전류가 그대로 | PM action 발생, driver 플래그·state 적용, UART·외부 회로 | DTS 선언만으로 저전력 진입을 증명하지 않기 |
| PM 없는 빌드에서 sleep lookup 실패 | CONFIG_PM, CONFIG_PM_DEVICE, skip 규칙 | phandle 누락으로 바로 단정하지 않기 |
이 글은 공식 코드의 경로를 재구성한 읽기 지도다. 특정 보드를 빌드·플래시하거나 오실로스코프로 핀 파형과 전류를 측정한 검증 기록은 포함하지 않는다.
6. Zephyr v3.7.0 고정 근거와 이어 읽기
| 고정 근거 | 확인한 부분 |
|---|---|
| 공식 Pin Control 가이드 | 하드웨어 모델, 독립 상태, DTS 표현, sleep 생략, 드라이버 계약 |
| 공식 v3.7.0 API | DEFINE/GET, apply, state ID와 오류 반환 |
| nRF52840 DK 보드 DTS · 핀 정의 | UART0의 state 연결과 P0.05–P0.08 실제 매핑 |
| v3.7.0 Nordic UARTE 드라이버 | config 생성, init의 default, 조건부 PM state 전환 |