본문 바로가기

BLE/펌웨어

[Noise Detector] nRF51 (5) : 배터리 잔량 계산

관련 링크

 

2017/12/09 - [회로/Noise Detector] - Noise Detector 2단계 -회로 (4) : 배터리 잔량 측정 회로

 

battery.zip
다운로드

 

배터리 전압은 사용할 수록 조금씩 떨어지는데, 이것을 이용하여 잔량을 구합니다.

그런데 일정하게 떨어지는 것이 아니라 몇개의 변곡점이 나타나는데요. 회로 카테고리에 포스팅된 배터리 잔량 회로에서 출력되는 전압을 그대로 측정하여 배터리 서비스를 사용하여 피어로 전송하면, nRF Logger 어플리케이션에 의해 로그에 기록되는데, 이 기록으로 누적시간에 대한 그래프로 만들어 보니 아래 그림과 같았습니다. 원본 파일은 위에 첨부했습니다.

 

완충시 측정되는 최대값인 210(3.98V)부터 제가 임의로 정한 최소값인 147까지 총 64범위(분해능)를 측정했습니다. 각 구간마다 전압 감소율이 다른데, 이때문에 전체 범위에 × 100 ÷ 64를 하면, 처음에는 잔량이 느리게 감소하다가 나중에는 한번에 확 떨어집니다. 따라서 2개의 변곡점을 기준으로 3구간으로 나누고 잔량 감소율을 다르게 적용하여, 사용자가 보기에 일정한 속도로 잔량이 감소하는 것처럼 보이게 했습니다.

 

연결이 됐을 때, LED가 켜지게 설정하였으며, LED를 껐을 경우 일주일까지 배터리가 지속됩니다.

 

잔량 연산은 분해능을 기준으로 계산되기 때문에 측정값에서 최소값(최대값 - 64)을 뺀 값에서 비교 연산이 시작됩니다. 

 

64부터 49까지 16 범위만 예로 들어보겠습니다. 

이 범위는 전압이 유지되는 시간이 제일 길기(완만하게 떨어지기) 때문에, 이 범위에서 1 감소는 다른 부분에서 1 감소보다 잔량 감소 효과가 더 큽니다.

그리고 측정값에서 최소값을 뺀 값이 이 범위에 있다면 무조건 15%는 확보했다는 의미이고, 이 15%는 나머지 48 범위를 의미합니다. 따라서  다시 48을 뺀 나머지 부분에 대해 85를 곱하고 16으로 나누어 줍니다. 그리고 여기에 15를 더하면 배터리 잔량이 계산됩니다.

그런데 /, * 연산자가 없는데요. Cortex-M0는 나눗셈기가 없기 때문에 컴파일러가 코드로 구현하게 됩니다. 이것은 CPU에 부담이 되기 때문에, 비트 이동 연산자를 사용해서 나눗셈을 구현했습니다. 이때 나누는 수는 2의 거듭 제곱이 되어야 합니다. 곱하는 수는 2의 거듭 제곱이 아니라도 상관 없습니다. 85 = 64 + 16 + 4 + 1로 표현할 수 있기 때문이죠. 

 

Cortex-M0에 곱셈기는 있습니다. 처음에는 없는줄 알고 비트이동 연산자로 구현했는데 여기에 있다고 나오네요. 곱셈(MUL) 명령어의 성능은 코어에 탑제된 곱셈기에 따라 달라집니다. 1사이클에 되는 경우도 있고 32사이클이 걸리는 경우도 있습니다. nRF51의 경우 single-cycle 곱셈기가 탑제되어 있습니다. 차이는 안나겠지만 아래 비트 이동 연산자로 만들어진 곱셈 기능은 '*' 연산자로 바꿔줘야 할 것 같네요.

 
전원은 측정값이 127이 될 때 까지도 유지되지만, 최소값을 146으로 설정한 것도 이 이유때문입니다. 연결 상태에서 LED를 꺼버리면 210에서 146까지 떨어지는데 일주일 정도 걸리는데 반해, 146에서 127로 떨어지는데 몇시간 걸리지 않기 때문에 별로 상관 없다고 생각했습니다. 오히려 리튬 배터리는 완전 방전 상태가 배터리 수명에 더 치명적이기 때문에 더 이득이지 않을까 생각합니다.
static uint8_t calculate_battery_level_64_resolution(int32_t battery_voltage, uint32_t* err_code)
{
	*err_code = NRF_SUCCESS;

	battery_voltage -= MIN_BATTERY_VOLTAGE; // 210 - 64 = 146

	if(battery_voltage > 48) // 64 - 49(16) : 100% - 15%
	{
		if(battery_voltage > 63)
			return 100;

		battery_voltage -= 48;
		return 15 + (((battery_voltage << 6) + (battery_voltage << 4) + (battery_voltage << 2) + battery_voltage) >> 4); // 15 + (battery_voltage x 85) ÷ 16
	}
	else if(battery_voltage > 16) // 48 - 17(32) : 15% - 2%
	{
		battery_voltage -= 16;
		return 2 + (((battery_voltage << 3) + (battery_voltage << 2) + battery_voltage) >> 5); // 2 + (battery_voltage x 13) ÷ 32
	}
	else if(battery_voltage > 0) // 16 - 1(16) : 2% - 0%
	{
		return (battery_voltage << 1) >> 4; // (battery_voltage x 2) ÷ 16
	}
	else // battery_voltage == 0
	{
		*err_code = NRF_ERROR_RESOURCES; // It means battery is discharged.
		return 0;
	}
}