Linux kernel ADC Battery

ADC란?

ADC란 Analog to Digital Converter의 약자로서 실생활에서 연속적으로 측정되는 신호를 프로세서가 그 값을 읽을 수 있도록 디지털 신호로 변환해주는 장치입니다.

ADC 변환

ADC 관련 용어

  • Sampling(표본화)
    아날로그 신호를 일정 주기로 추출하는 과정
    연속시간 → 이산시간

  • Quantizing[양자화, 분해능]
    표본화된 각각의 신호의 대표값을 변환하는 과정
    연속적인 신호의 크기 -> 이산적인 신호의 크기
    10bit,8bit의 고정된 분해능이 있다.

Sampling(표본화)

Sampling

Quantizing [양자화]

분해능 8bit 10bit
표현개수 256개 (0~255) 1024개 (0~1023)
  • 예 : ADC의 기준 전압이 5V일때 bit당 전압은?
    • 8bit : 5V/256 = 0.01953125 V/bit
    • 10bit : 5V/1024 = 0.0048828125 V/bit

Quantizing

전압 분배

예제에 쓰인 배터리의 전압은 7.4V입니다. 보통 최대 전압이 이와 같이 높지 않기 때문에 전압 분배기를 만들어 해결하는 것이 보통입니다.
저항이 만나는 접점은 두 전압을 합친 전압의 절반을 같습니다. 총 전압이 V인 두개의 저항(R1 및 R2)이 직렬로 연결된 경우 R1의 전압은 다음과 같습니다.

V1 = (V*R1)/(R1 + R1)

ADC의 배터리 전압을 절반으로 줄이는 간단한 전압 분배기

이 접근법의 단점은 배터리가 전압 분배기를 통해 지속적으로 방전된다는 것입니다. 그러나 큰 저항 값을 사용하면 전류를 낮게 유지할 수 있습니다.

rk3328 ADC 스팩

  • 6-channel signal-ended
  • 10bit
  • 50KS/s sampling

rk30_factory_adc_battert 드라이버 분석

DTS 분석

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&adc {
status = "okay";

adc-battery {
status = "okay";
compatible = "rk30-adc-battery";
io-channels = <&adc 1>, <&adc 3>;
dc_det_gpio = <&gpio2 GPIO_C3 GPIO_ACTIVE_LOW>;
auto_calibration = <0>;
ref_voltage = <3300>;
led-gpio = <&gpio3 GPIO_A2 GPIO_ACTIVE_LOW>;
//bat_low_gpio = <&gpio0 GPIO_A7 GPIO_ACTIVE_LOW>;
chg_ok_gpio = <&gpio2 GPIO_C0 GPIO_ACTIVE_HIGH>;
bat_table = <0x0000 0x0000 0x0000 0x0000 0x01d6 0x005d
0x283c 0x29cc 0x2a80 0x2b3e 0x2bc0 0x2c2e 0x2cc4 0x2da0 0x2e7c 0x2f50 0x3048
0x2ac6 0x2bca 0x2c60 0x2cec 0x2d82 0x2e0e 0x2e72 0x2f26 0x2fda 0x30c0 0x3138>;
is_dc_charge = <1>;
is_usb_charge = <0>;
};
};
  • status : 해당 부분의 사용 여부
  • compatible : 해당 값과 일치하는 compatible가진 드라이버를 사용. 여기서는 rk30_factory_adc_battert드라이버
  • io-channels : adc channels
  • dc_det_gpio : soc와 연결된 dc_det gpio port. DC irq 관련
  • auto_calibration : voltage 계산시 DTS에 지정된 ref_voltage의 값을 쓸지 여부 판별
  • ref_voltage : voltage과 관계된 기준 전압값. 입력전압과 기준전압을 비교하여 디지털로 변환한다. 0 ~ Vref 측정 가능. Vref = 0xffffffffff(10bit일때)
  • led-gpio : ??
  • chg_ok_gpio : 충전중의 여부를 판별
  • bat_table : 충전량을 판별하기 위한 table.
    0-3은 쓰이지 않음.
    4-5는 ADC 샘플링 전압 분배기 저항(4는 pull_up, 5는 pull_down).
    6-16은 충전중이 아닐때 최소-최대 전압값
    17-27은 충전중일때 최소-최대 전압값
  • is_dc_charge : dc 충전을 사용하는지 여부
  • is_usb_charge : usb 충전을 사용하는지 여부

voltage값 구하는 부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static void rk30_adc_battery_voltage_samples(struct rk30_adc_battery_data *bat)
{
int value;
int i,*pStart = bat->adc_samples, num = 0;
int level = bat->charge_level;


value = bat->adc_val; //adc 측정값
bat->adc_val = rk_adc_battery_iio_read(bat->pdata);

*(bat->pSamples++) = rk_adc_voltage(bat,value); //adc 측정값을 저장(samplig 때문)

bat->bat_status_cnt++;
if (bat->bat_status_cnt > NUM_VOLTAGE_SAMPLE) bat->bat_status_cnt = NUM_VOLTAGE_SAMPLE + 1;

num = bat->pSamples - pStart;

if (num >= NUM_VOLTAGE_SAMPLE){
bat ->pSamples = pStart;
num = NUM_VOLTAGE_SAMPLE;
}

value = 0;
for (i = 0; i < num; i++){
value += bat->adc_samples[i];
}
bat->bat_voltage = value / num; //sampling값에 따른 기준값의 평균값을 구함

/*handle ripple */
if(0 == bat->pdata->use_board_table){
if(1 == level){ //충전중
if(bat->bat_voltage >= batt_table[2*BATT_NUM +5]){ //측정 값이 테이블의 최대값 이상시 테이블 최대값으로 조정
bat->bat_voltage = batt_table[2*BATT_NUM +5];
}
else if(bat->bat_voltage <= batt_table[BATT_NUM +6]){ //측정 값이 테이블의 최소값 이하시 테이블 최소값으로 조정
bat->bat_voltage = batt_table[BATT_NUM +6];
}
}
else{ //충전X
if(bat->bat_voltage >= batt_table[BATT_NUM +5]){ //측정 값이 테이블의 최대값 이상시 테이블 최대값으로 조정
bat->bat_voltage = batt_table[BATT_NUM +5];
}
else if(bat->bat_voltage <= batt_table[6]){ //측정 값이 테이블의 최소값 이하시 테이블 최소값으로 조정
bat->bat_voltage = batt_table[6];
}
}
}else{
rk_handle_ripple(bat, level);
}
}
공유하기