지난 글 요약
지난 글에서 Alert 설정을 잡았다. 이제 1개짜리 EVAL 보드는 완벽하게 동작한다.
12셀이 아니라 24셀, 48셀을 모니터링하려면? AD7280A를 여러 개 데이지체인으로 연결해야 한다.
데이지체인 구조
AD7280A는 최대 8개까지 데이지체인으로 연결할 수 있다. 48셀 모니터링 가능.

- Dev 0: 배터리 스택 하위 (Cell 1~6)
- Dev 1: 배터리 스택 상위 (Cell 7~12)
- SDO → SDI로 체인 연결
증상
EVAL 보드 2개를 연결했다. 1개일 때 잘 되던 코드를 그대로 돌렸더니:
Dev 0 Cell 1: 4012 mV ✓
Dev 0 Cell 2: 4008 mV ✓
...
Dev 0 Cell 6: 4015 mV ✓
Dev 1 Cell 1: 0 mV ✗
Dev 1 Cell 2: 0 mV ✗
...
Dev 1 Cell 6: 0 mV ✗
Dev 0은 잘 읽히는데 Dev 1이 전부 0이다.
삽질 1: 주소 할당을 안 했다
1개일 때는 주소가 0으로 고정이라 신경 안 썼다. 근데 2개 이상이면 각 디바이스에 고유 주소를 할당해야 한다.
AD7280A는 재밌는 자동 주소 할당 메커니즘이 있다.
// Control LB 레지스터
#define AD7280A_CTRL_LB_INC_DEV_ADDR (1 << 1) // 주소 증가
#define AD7280A_CTRL_LB_LOCK_DEV_ADDR (1 << 2) // 주소 잠금
주소 할당 순서
모든 디바이스에 INC_DEV_ADDR = 1 전송
- 각 디바이스가 주소를 1씩 증가시키며 전달
- Dev 0 = 0, Dev 1 = 1, Dev 2 = 2…
모든 디바이스에 LOCK_DEV_ADDR = 1 전송
- 주소 고정
void ad7280a_assign_addresses(uint8_t num_devices) {
// 1단계: 주소 증가 활성화
uint8_t ctrl_lb = AD7280A_CTRL_LB_MUST_SET |
AD7280A_CTRL_LB_INC_DEV_ADDR;
uint32_t cmd = ad7280a_crc_write(
((uint32_t)AD7280A_REG_CONTROL_LB << 21) |
((uint32_t)ctrl_lb << 13) |
(1 << 12) // All devices
);
// num_devices번 전송 (각 디바이스가 주소 증가)
for (int i = 0; i < num_devices; i++) {
ad7280a_transfer_32bits(cmd);
}
// 2단계: 주소 잠금
ctrl_lb = AD7280A_CTRL_LB_MUST_SET |
AD7280A_CTRL_LB_LOCK_DEV_ADDR;
cmd = ad7280a_crc_write(
((uint32_t)AD7280A_REG_CONTROL_LB << 21) |
((uint32_t)ctrl_lb << 13) |
(1 << 12)
);
ad7280a_transfer_32bits(cmd);
}
삽질 2: 데이지체인 리드백 활성화 안 함
주소 할당했는데도 Dev 1 데이터가 안 읽힌다. DAISY_CHAIN_RB_EN 비트를 켜야 했다.
#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN (1 << 0)
이 비트가 0이면 데이터가 체인을 타고 MCU까지 전달되지 않는다.
// Control LB 최종 설정
uint8_t ctrl_lb = AD7280A_CTRL_LB_MUST_SET | // D4 = 1
AD7280A_ACQ_TIME_1600NS | // D6:D5 = 11
AD7280A_CTRL_LB_LOCK_DEV_ADDR | // D2 = 1
AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN; // D0 = 1 ← 중요!
삽질 3: VIN6-VIN0 연결 문제
데이터시트 Figure 29를 보면, 데이지체인에서 Dev 0의 VIN6와 Dev 1의 VIN0가 같은 전압이어야 한다.
배터리 스택:
Cell 12 ─── Dev1-VIN6 (36V)
Cell 11
Cell 10
Cell 9
Cell 8
Cell 7 ─── Dev1-VIN0 = Dev0-VIN6 (18V) ← 같은 노드!
Cell 6 ─── Dev0-VIN6 (18V)
Cell 5
Cell 4
Cell 3
Cell 2
Cell 1 ─── Dev0-VIN0 (0V, GND)
EVAL 보드 2개를 테스트할 때 저항 분배기를 따로따로 만들었더니 이 연결이 끊어져 있었다.

해결: J2-7 (Dev0 VIN6)과 J3-1 (Dev1 VIN0)을 물리적으로 연결.
초기화 순서 정리
데이지체인 초기화는 순서가 중요하다:
void ad7280a_daisy_chain_init(uint8_t num_devices) {
// 0. 하드웨어 리셋 (PD 핀 토글)
gpio_put(PIN_PD, 0);
sleep_ms(1);
gpio_put(PIN_PD, 1);
sleep_ms(1);
// 1. 소프트웨어 리셋 (모든 디바이스)
ad7280a_software_reset();
sleep_ms(1);
// 2. 주소 할당
ad7280a_assign_addresses(num_devices);
// 3. Control LB 설정 (데이지체인 리드백 활성화)
uint8_t ctrl_lb = AD7280A_CTRL_LB_MUST_SET |
AD7280A_ACQ_TIME_1600NS |
AD7280A_CTRL_LB_LOCK_DEV_ADDR |
AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN;
ad7280a_write_all(AD7280A_REG_CONTROL_LB, ctrl_lb);
// 4. Control HB 설정 (변환 모드)
uint8_t ctrl_hb = AD7280A_CONV_ALL |
AD7280A_CONV_AVG_8;
ad7280a_write_all(AD7280A_REG_CONTROL_HB, ctrl_hb);
}
데이터 읽기 순서
데이지체인에서 데이터는 Dev N → Dev N-1 → … → Dev 0 → MCU 순서로 나온다.
void ad7280a_read_all_cells(uint8_t num_devices) {
// 변환 시작
gpio_put(PIN_CNVST, 0);
sleep_us(1);
gpio_put(PIN_CNVST, 1);
sleep_us(50); // 변환 완료 대기
// 모든 디바이스의 모든 채널 읽기
// 총 전송 횟수 = num_devices * 6 (셀) + 여유분
for (int i = 0; i < num_devices * 8; i++) {
uint32_t rx = ad7280a_transfer_32bits(AD7280A_READ_CMD);
uint8_t dev_addr = (rx >> 27) & 0x1F;
uint8_t reg_addr = (rx >> 23) & 0x0F;
uint16_t data = (rx >> 11) & 0x0FFF;
if (reg_addr <= 5) { // Cell voltage register
float voltage = data * 0.976f + 1000.0f;
printf("Dev%d Cell%d: %.0f mV\n",
dev_addr, reg_addr + 1, voltage);
}
}
}
결과
초기화 순서 맞추고 VIN6-VIN0 연결하니까:
Dev 0 Cell 1: 4012 mV ✓
Dev 0 Cell 2: 4008 mV ✓
...
Dev 0 Cell 6: 4015 mV ✓
Dev 1 Cell 1: 4005 mV ✓
Dev 1 Cell 2: 4011 mV ✓
...
Dev 1 Cell 6: 4009 mV ✓
12셀 전부 읽힌다!
삽질 포인트 정리
- 주소 할당 필수 - INC_DEV_ADDR → LOCK_DEV_ADDR 순서
- DAISY_CHAIN_RB_EN = 1 - 이거 안 켜면 데이터 안 나옴
- VIN6-VIN0 연결 - 체인 사이 전압 노드 공유
- 초기화 순서 - 리셋 → 주소 할당 → 설정
다음 글에서는 주소 할당 메커니즘을 더 자세히 파본다.