상세 컨텐츠

본문 제목

Real Time Clock(RTC)

CS지식 학습

by Tabris4547 2025. 5. 27. 21:15

본문

728x90

 

RTC(Real Time Clock)

 

이름에서 알 수 있듯이, 현재 시간을 알려주는 시계입니다.

리눅스에서 date명령어를 입력할 때

RTC에서 HW시간을 바탕으로 현재 시간을 알려줍니다.

그래서 RTC로 측정한 시간을 HW time이라고도 부릅니다.

 

왜 RTC가 필요한가? RTC사용경험

 

제가 RTC를 사용했던 건 모뎀의 알람을 맞추기 위해서였습니다.

구현해야할 기능은 

특정 시간대에 모뎀이 (전원선이 연결되어있지만 전원이 OFF일 때)

알람처럼 켜지는 기능이었습니다.

예를들면 매일 아침 9시에 모뎀을 키는 식이죠.

그런데 여기서 '기기가 OFF되어있을때 켜진다'라는 것이 문제였습니다.

기기가 항상 켜있다면

'얼마 뒤에 알람을 울려라'라고 하면 되겠지만

아예 기계 자체가 OFF가 되어있다면

프로그램 자체가 동작하지 않는데

이를 어떻게 해야할지 막막했습니다.

이 때 사용했던 것이 RTC로써

HW내부의 RTC를 사용해서 알람기능을 구현했습니다.

 

RTC의 특징

 

RTC는 HW모듈이 꺼저있어도 시간을 측정할 수 있습니다.

메인 전원이 꺼저있어도 별도의 건전지로 동작합니다.

RTC 동작에 필요한 전력이 매우 작기 때문에

RTC는 메인전원과 별도로 움직일 수 있습니다.

그래서 모듈을 껏다가 킨 경우에도

그동안의 HW Time을 바탕으로 현재 시간을 계산할 수 있습니다.

 

 

어떻게 사용했었나

 

1. 기능지원여부

 

가장 먼저는 시스템 Level에서 RTC를 사용할 수 있는지 여부를 알아봤습니다.

팀장님께 가이드를 받아보니

"RTC가 동작을 안할 수 있다. 다른 팀에 문의를 해서 커널단에서 지원되는지 봐야한다"

라고 말씀해주셨습니다.

그래서 해당 팀의 담당자 분께 RTC지원여부를 여쭤보셨습니다.

제가 회사에서 사용하는 모뎀이 퀄컴 칩 베이스로 동작하고

32bit/64bit 두 가지 타입으로 나뉘어져있기 때문에

퀄컴에서 해당 기능을 몇 비트 OS에서 지원되는지 여쭤봤습니다.

다행히도 해당 기능들이 작동하고 있음을 전달받았습니다.

 

 

2. 코드구현 따라가보기

 

그 다음에는 RTC관련 시스템 함수들을 사용했습니다.

회사에서 사용했던 코드 중 일부를 넣었습니다.

자세한 코드는 대외비 이슈로 생략했고

어떤 식으로 RTC를 사용했는지 중심으로 적었습니다.

int rtc_fd = open("/dev/rtc", O_RDONLY);

 

가장 먼저, RTC의 파일시스템을 오픈해야합니다.

위와 같이 open함수를 사용해 해당 지점에 저장된 rtc의 fd를 가져옵니다.

 

struct rtc_time time;
if (ioctl(rtc_fd, RTC_SET_TIME, &time) == -1 )
{
	return FAIL;
}

rtc_time 구조체를 선언합니다.

이 구조체는 rtc로 얻은 시간정보를 받을 수 있습니다.

ioctl을 통해 현재 rtc정보를 읽어올 수 있습니다.

다음과 같이 fd에는 rtc_fd, Request에는 RTC_SET_TIME을 Input으로 넣는다면

time구조체에 rtc시간 정보가 입력됩니다.

만약 ioctl 결과값이 -1이라면 실패한 경우이므로 예외처리를 해야합니다. 

struct rtc_time {
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;     /* 사용 안 함 */
    int tm_yday;     /* 사용 안 함 */
    int tm_isdst;    /* 사용 안 함 */
};

struct rtc_time은 다음과 같은 구조를 가지고 있습니다.

초/분/시간/날짜/월/년도

각각의 데이터를 받아옵니다.

저같은 경우에는 알람시간을 설정할 때

현재의 rtc시간을 받은 후

'얼마 뒤에 알람이 울릴지'를 더했습니다.

만약 10초 뒤에 알람이 울릴 예정이라면

tm_sec에 10을 더하고 

60을 넘기면 초에서 60을 빼주고

분을 하나 더하는 식으로요.

 

struct rtc_wkalrm {
    unsigned char enabled;
    unsigned char pending;
    struct rtc_time time;
};

 

알람시간 정보를 담은 rtc_wkalrm 구조체입니다.

rtc_time 구조체를 받으면서

enabled,pending이라는 변수를 받습니다.

enabled 변수는 알람을 읽고 끄거나 현재상태를 알고자할때

pending 변수는 미처리 인터럽트를 확인할 때 사용이 됩니다.

 

if( ioctl( rtc_fd, RTC_WKALM_SET, &alarm ) == -1 )
{
  return FAIL;
}
if( ioctl( rtc_fd, RTC_AIE_ON, 0 ) == -1 )
(
  return FAIL;
}

RTC_WKALM_SET request는 이름에서 유추할 수 있듯이

알람을 Setting하는 인터페이스입니다.

RTC_AIE_ON은 알람 인터럽트를 켜는 인터페이스로

해당 인터페이스를 사용해야

알람시간이 되었을 때 알람 인터럽트가 발생합니다.

res = pthread_create( &handle->alarm_listen_thrd, &rtc_alarm_thrd_attr,
                      ( void * )rtc_alarm_listener, ( void * )&listen );

알람인터럽트까지 대기하는 동작은 쓰레드로 구현했습니다.

프로세스로 구현할 경우, 알람시간까지 프로그램 전체가 대기를 해야합니다.

제가 회사에서 만드는 프로그램은 모뎀에서 동작하는 프로그램인데,

프로세스로 구현을 한다면 알람 하나 대기하다가 다른 동작을 할 수 없습니다.

프로세스로 구현하면 동기적으로 동작이 수행되기 때문에

알람호출까지 대기->알람->다른 동작 이렇게 순서가 됩니다.

이러면 모뎀에 다른 기능을 쓸 수가 없기 때문에

알람을 울리는 기능은 쓰레드로 구현했습니다.

 

unsigned long data;
read_res = read( rtc_fd, &data, sizeof( unsigned long ) );

if(read_res == -1)
{
/*에러리턴*/
}
else
{
/*알람울림*/
}

알람 인터럽트까지 대기하는 rtc_alarm_listener에서

알람까지 대기하는 부분입니다.

다음과 같이 read함수를 사용하면

알람시간이 되었을 때 인터럽트가 수행이 됩니다.

void rtc_alarm_read( M2MB_ATP_HANDLE atpHandle )
{

  pthread_t init_thread;
  pthread_attr_t rtc_alarm_thrd_attr;
  FILE *fp = popen( "dmesg | grep -i 'PMIC PON log: PON Trigger'", "r" );
  
  /*중략*/
  
  printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot read_Start" );

  if( fp == NULL )
  {
    printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot : Dmesg error" );
    return;
  }
  while( fgets( buffer, sizeof( buffer ), fp ) != NULL )
  {
    if( strstr( buffer, "PON Trigger: RTC_ALARM" ) )
    {
      strncpy( last_trigger, "RTC_ALARM", sizeof( last_trigger ) - 1 );
    }
    else if( strstr( buffer, "PON Trigger: HARD_RESET" ) )
    {
      strncpy( last_trigger, "HARD_RESET", sizeof( last_trigger ) - 1 );
    }
    else if( strstr( buffer, "PON Trigger: KPDPWR_N") )
    {
      strncpy( last_trigger, "KPDPWR_N", sizeof( last_trigger ) - 1 );
    }
  }

  if( strcmp( last_trigger, "RTC_ALARM" ) == 0 )
  {
    printf( M2MB_TC_RTC,M2MB_TL_LOG, "Boot Reason: RTC_ALARM detected (alarm wakeup)" );
    fd = fopen( ALARM_FILE_PATH, "r" );
    if( !fd )
    {
      printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot: Failed to open file" );
      return ;
    }
    printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot: File open");
    
    /*후략*/

Boot reason이 알람때문인지 아닌지 확인하는 코드입니다.

확인방법을 서칭하여 로그를 통한 확인 방법을 채택했습니다.

모듈에서 Boot가 될 때 log가 찍히는데

이때에 Boot reason이 함께 찍힌다는 걸 알고

알람으로 인한 REBOOT

REBOOT명령어에 의한 REBOOT

모듈 전원버튼 Push에 의한 REBOOT

총 3가지의 경우 어떤 메세지가 나오는지 파악한 후

RTC ALARM에 대한 REBOOT 메세지가 최종적으로 찍힌다면

알람에 의한 REBOOT이라고 판단하고 그에 따른 동작을 수행하도록 코드를 짰습니다.

 

if( ioctl( rtc_fd, RTC_WKALM_RD, &rtc_alarm ) != -1 )
{
 rtc_alarm.enabled = 0;
 
 if( ioctl( rtc_fd, RTC_WKALM_SET, &rtc_alarm ) != -1 )
 {
     ret = 0;
  }
  
 }

 

알람을 지우는 부분입니다.

rtc_wkalrm 구조체에 있는 enabled를 0으로 만들어

알람 인터럽트를 하지 않도록 만들 수 있습니다.

728x90

'CS지식 학습' 카테고리의 다른 글

메모리 가상화(memory virtualization)  (0) 2024.04.29
GIC  (0) 2024.04.18
스택카나리(Stack canary)  (0) 2024.04.10
시스템 콜의 ftrace 분석을 해보자!  (0) 2024.04.02
익셉션(Exception)  (0) 2024.03.15

관련글 더보기

댓글 영역