조용하다고 정상은 아니었다 — 사라진 ADC와 2.5V 기준전압
PLC 모뎀 작업을 일단락하고 충전기의 핵심으로 넘어갔다. 차량과 대화하는 CP(Control Pilot) 신호다. CP 전압을 읽으려면 ADC가 맞아야 하고, ADC가 맞으려면 기준전압이 맞아야 한다.
SC6_0의 DTS에는 9월에 이런 확인 절차를 적어 두었다.
dmesg | grep -i vrefbuf
- 조용하면 VREFBUF가 2.5V 확보 -> 현재 설정 그대로
- "timed out!" VREF+가 외부에 묶임 -> vref-supply = <&vddref>
실행해 보니 아무것도 나오지 않았다. 조용하니 정상이라고 생각했다. 틀린 판단이었다.
1. ADC가 없다
값을 읽으려고 IIO 장치 목록을 열었다.
root@myir:~# grep -H . /sys/bus/iio/devices/iio:device*/name
/sys/bus/iio/devices/iio:device0/name:40000000.timer:timer@1
/sys/bus/iio/devices/iio:device1/name:40002000.timer:timer@3
/sys/bus/iio/devices/iio:device2/name:44000000.timer:timer@0
/sys/bus/iio/devices/iio:device3/name:44001000.timer:timer@7
넷 다 타이머(PWM용 트리거)다. ADC가 없다. VREFBUF가 조용했던 건 정상이라서가 아니었다. 기준전압을 켜 볼 ADC가 아예 없어서였다.
커널 설정부터 확인했다.
CONFIG_STM32_ADC_CORE=y
CONFIG_STM32_ADC=y
CONFIG_REGULATOR_STM32_VREFBUF=y
드라이버는 커널에 들어 있다. 보드에서도 등록돼 있었다.
root@myir:~# ls /sys/bus/platform/drivers/ | grep -i adc
stm32-adc
stm32-adc-core
...
root@myir:~# cat /sys/kernel/debug/devices_deferred
cpufreq-dt
48003000.adc
devices_deferred는 필요한 무언가를 기다리다가 끝내 올라오지 못한 장치 목록이다. ADC(48003000.adc)가 거기 있었다.
2. 에러 없이 기다리는 드라이버
stm32-adc-core.c의 probe를 보면 이유가 보인다. 필요한 자원을 하나씩 요청하는데, 모두 같은 패턴이다.
priv->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "vref get failed, %d\n", ret);
return ret;
}
-EPROBE_DEFER(“아직 준비 안 됐으니 나중에 다시”)일 때는 에러를 찍지 않는다. 부팅 중에는 순서상 흔한 일이라 일부러 조용히 넘기는 것이다. 문제는 그 “나중”이 영원히 오지 않을 때다. 그러면 로그 한 줄 없이 장치가 사라진다.
기다릴 만한 후보는 여섯 개였다. 전원 셋(vdd, vdda, vref), 클럭 둘, 시스템 설정 하나.
3. 단서는 이미 로그에 있었다
부팅 로그에 계속 보이던 줄이 있었다.
[ 34.408433] vddref: disabling
나는 이걸 “쓰지 않는 전원을 커널이 정리하는 정상 메시지”라고 설명했었다. 커널은 부팅이 끝날 무렵 아무도 쓰지 않는 레귤레이터를 끄는데, 그 메시지는 맞다. 하지만 vddref는 ADC의 vdd/vdda 전원이다. ADC가 정상으로 올라왔다면 이 전원을 켜서 쓰고 있었을 것이고, 커널이 끌 일이 없다.
이 줄은 “ADC가 안 올라왔다”는 증거였다. 동시에 vddref는 존재한다는 증거이기도 하다. 그러니 기다리는 대상은 vddref가 아니다.
4. 끊긴 고리
DTB를 역컴파일해서 전원 연결을 따라갔다.
ADC ──vref──▶ vrefbuf (2.5V) ──vdda-supply──▶ vdd (buck3) ──▶ ✘
MYiR의 공통 DTSI에 이렇게 적혀 있었다.
&vrefbuf {
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
vdda-supply = <&vdd>;
status = "okay";
};
vdd는 buck3, 즉 원래 ST 전원 관리 칩(STPMIC1)의 출력 이름이다. 그런데 이 모듈의 보드 DTS에서는 이렇게 정의돼 있다.
regulators {
compatible = "simple-bus";
...
vdd: buck3 {
regulator-name = "vdd";
regulator-min-microvolt = <3300000>;
...
};
compatible이 없는 노드다. PMIC 드라이버가 붙는 자리가 아니라, 다른 보드 DTS에서 옮겨 온 흔적처럼 이름만 있는 빈 노드다. 이 모듈은 PMIC 없이 고정 전원으로 구성된 것으로 보인다. 어떤 드라이버도 이 노드를 레귤레이터로 등록하지 않는다.
그러면 이런 일이 생긴다.
vrefbuf자체는 등록된다. 자기 공급원은 나중에 찾으면 되기 때문이다.- ADC가
vref로vrefbuf를 요청하는 순간, 커널은vrefbuf의 공급원(vdd)까지 찾아야 한다. - 영원히 못 찾으니
-EPROBE_DEFER가 난다. 에러는 찍히지 않는다.
9월에 &adc의 vref-supply를 vrefbuf로 바꾼 순간부터 ADC는 사라져 있었다.
5. 이어 보니 진짜 답이 나왔다
vrefbuf의 공급원을 실제로 등록되는 3.3V 고정 레귤레이터 vddref로 이어 주었다.
&vrefbuf {
vdda-supply = <&vddref>;
};
재부팅하자 처음으로 진짜 판별이 나왔다.
[ 1.840164] vref: supplied by vddref
[ 1.953689] regulator regulator.6: stm32 vrefbuf timed out!
[ 1.953727] stm32-adc-core 48003000.adc: vref enable failed
[ 1.954069] stm32-adc-core: probe of 48003000.adc failed with error -110
공급원 연결은 해결됐다. 이제 ADC가 기다리지 않고 실제로 vrefbuf를 켰다. 그런데 2.5V가 준비됐다는 신호(VRR)가 오지 않았다.
드라이버는 이 경우를 대비해 두었다.
ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val,
val & STM32_VRR, 650, 10000);
if (ret) {
dev_err(&rdev->dev, "stm32 vrefbuf timed out!\n");
val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
val = (val & ~STM32_ENVR) | STM32_HIZ;
writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
}
10ms 안에 준비되지 않으면 버퍼를 끄고 핀을 놓아 준다(HIZ). 외부 전압과 맞붙어도 길어야 10ms라 하드웨어가 상할 일은 없다.
timed out!의 의미는 9월에 적어 둔 그대로다. 모듈이 VREF+ 핀을 외부 전압(VDDA 3.3V로 추정)에 묶어 두었다. 내부 2.5V 기준전압은 쓸 수 없다. SC6_0 넷리스트를 다시 봐도 모듈 커넥터에 VREF+나 VDDA 핀은 없고, ADC 관련 핀은 ANA0(84번)과 ANA1(83번)뿐이다. 캐리어 보드 쪽에서는 손댈 방법이 없다.
6. MYiR가 맞았다
9월의 DTS 주석에는 이렇게 적혀 있었다.
MYiR BSP는 &vrefbuf를 2.5V로 켜두면서도 &adc의 vref-supply는 3.3V 고정 레귤레이터(&vddref)로 물려두었다. 둘 다 참일 수 없는 모순이고…
모순이 아니었다. vrefbuf 노드는 ST 레퍼런스에서 물려받은 설정이 남은 것이고, 실제 ADC는 3.3V로 쓰도록 맞춰 둔 것이었다. 최종 설정은 이렇다.
&vrefbuf {
status = "disabled";
};
&adc {
/delete-property/ pinctrl-names;
/delete-property/ pinctrl-0;
vref-supply = <&vddref>;
status = "okay";
...
};
| 9월 | 10/21 오전 | 10/21 최종 | |
|---|---|---|---|
| ADC 기준전압 | vrefbuf |
vrefbuf |
vddref (3.3V) |
vrefbuf 공급원 |
vdd (빈 노드) |
vddref |
(꺼짐) |
| 결과 | ADC 무한 대기 | timed out! → ADC 실패 |
ADC 정상 |
7. 값 읽기
root@myir:~# grep -H . /sys/bus/iio/devices/iio:device*/name
...
/sys/bus/iio/devices/iio:device4/name:48003000.adc:adc@0
root@myir:~# cat /sys/bus/iio/devices/iio:device4/in_voltage_scale \
/sys/bus/iio/devices/iio:device4/in_voltage0_raw \
/sys/bus/iio/devices/iio:device4/in_voltage1_raw
0.050354003
967
21981
scale 0.050354003은 3300mV를 16비트(65536칸)로 나눈 값이다. 커널이 3.3V 기준으로 계산하고 있다는 뜻이다.
| 신호 | raw | 전압 (raw × scale) |
|---|---|---|
| AD_CP (ANA0) | 967 | 약 0.05V |
| AD_TEMP (ANA1) | 21981 | 약 1.11V |
만약 기준전압이 2.5V라고 잘못 믿은 채로 ADC가 올라왔다면, AD_TEMP를 0.84V로 읽었을 것이다. 9월 주석에 적어 둔 “에러 없이 값만 32% 어긋나는” 경우가 바로 이것이다. 이번에는 ADC가 아예 안 올라오는 바람에 오히려 문제가 드러났다.
AD_CP가 0.05V로 바닥에 붙은 건 PWM을 아직 켜지 않아서다. CP 출력이 -12V 쪽에 있다.
8. CP PWM으로 AD_CP 움직여 보기
CP 출력은 PA15(TIM2_CH1)이고, 보드에서는 pwmchip0으로 잡힌다(TIM2 주소 40000000).
echo 0 > /sys/class/pwm/pwmchip0/export
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period # 1kHz
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 100% = +12V 고정
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
cat /sys/bus/iio/devices/iio:device4/in_voltage0_raw
echo 0 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle # 0% = -12V 고정
cat /sys/bus/iio/devices/iio:device4/in_voltage0_raw
듀티 0%와 100%에서 AD_CP 값이 바뀌는 것을 확인했다. PWM 출력 → CP 구동 회로 → AD_CP 측정의 경로가 처음부터 끝까지 살아 있다.
충전기는 9V를 만들 수 없다
시험하면서 궁금했던 게 있다. 충전 중에는 CP가 9V나 6V가 되는데, 그걸 충전기가 바꾸는 걸까?
아니다. CP는 충전기와 차량이 한 선을 같이 쓰는 분압 회로다.
충전기 차량
±12V ──[1kΩ]──┬────── CP ──────┬──|>|──┬──[2.74kΩ]── PE
│ │ └──[1.3kΩ]──S2── PE
AD_CP │
충전기는 ±12V를 1kΩ을 거쳐 내보내기만 한다. 차량이 저항을 걸면 High 전압이 9V(2.74kΩ, State B)나 6V(1.3kΩ 추가, State C)로 내려간다. 충전기가 정하는 것은 듀티뿐이다. 듀티는 차량에 허용 전류를 알리는 데 쓰고, 10~85% 구간에서는 듀티(%) × 0.6 = A다. 예를 들어 26.7%면 16A다.
그래서 차 없이 시험하려면 차량 쪽을 흉내 내면 된다. 다이오드 + 2.7kΩ을 CP와 PE 사이에 걸면 9V, 여기에 1.3kΩ을 병렬로 더하면 6V다. 다이오드는 실제 차량에도 들어 있는 부품이다. PWM의 Low 쪽을 -12V로 유지시켜서, 충전기가 “진짜 차량”인지 확인하는 근거가 된다.
9. 설계자에게 공유할 것
설계자는 9월에 VREF = 2.5V로 확인해 주었고, 캐리어 회로도 그 기준으로 맞춰져 있다(R138 분압 1.243V = 2.5V의 절반 등). 실제로는 3.3V다.
| 영향 | 내용 |
|---|---|
| 측정 범위 | AD_CP 최대 약 2.66V도 3.3V 안이라 잘리지 않는다. 2.5V였다면 오히려 위쪽이 잘릴 위험이 있었다 |
| 전압 환산 | 소프트웨어는 3.3V 기준으로 계산해야 한다 |
| 분해능 | 2.5V 기준 대비 약 24% 낮아진다 |
회로를 고칠지는 설계자가 판단할 일이다.
10. 남은 것
| 항목 | 메모 |
|---|---|
| 멀티미터 대조 | C114(AD_CP), C76(AD_TEMP) 전압과 ADC 값을 비교해 3.3V 기준을 최종 확정 |
| CP 상태별 기준표 | +12V / -12V / 9V / 6V일 때의 AD_CP raw 값. 충전 프로그램이 상태를 판단할 기준 |
| 설계자 공유 | 기준전압 3.3V, 분해능 감소 |
| AD_TEMP 환산 | NTC 규격을 확인해 전압을 온도로 바꾸는 식 |
정리
"vrefbuf 조용함 = 정상" → ADC가 없어서 조용했던 것
devices_deferred 에 adc → 무언가를 에러 없이 무한 대기
vddref: disabling → ADC가 전원을 안 쓰고 있다는 증거
vrefbuf → vdd(buck3) → 빈 노드 → PMIC 흔적만 남은 노드, 영원히 못 찾음
공급원을 이어 보니 timed out! → VREF+는 모듈에서 외부에 묶여 있음
MYiR의 "모순" → 이유 있는 설정이었다
이번에 배운 것은 두 가지다.
판별 명령이 조용할 때는, 그 판별을 할 주체가 살아 있는지부터 봐야 한다. “에러가 없다”와 “에러를 낼 기회가 없었다”는 로그로는 구별되지 않는다. devices_deferred와 /sys/bus/iio 같은 “있어야 할 것이 있는지” 확인이 같이 가야 한다.
벤더 설정이 이상해 보이면, 고치기 전에 그 이유를 먼저 찾아야 한다. 9월의 나는 MYiR의 설정을 모순이라고 고쳤고, 한 달 뒤 같은 자리로 되돌아왔다.
댓글남기기