RTC DS1302 linux 예제 C 코드
본 글은 시간 저장을 위해 사용하는 RTC 칩 중 하나인 DS1302의 리눅스 예제 C 코드에 대해 기술한다.
DS1302의 datasheet를 보면 아래와 같은 구조로 연결이 이루어진다.
아래 그림처럼 데이터를 읽고 쓸 때 CE핀을 high로 올려주고, 읽고 쓰지 않을 때는 low로 내려준다. SCLK과 I/O핀은 아래 그림과 같이 일반적인 i2c나 spi에서 데이터를 읽고 쓸 때 사용한다. 본 글의 예제 코드는 gpio를 컨트롤 하여 아래 그림의 데이터 전송 형식대로 구현 했다.
DS1302 example c code
연결된 핀의 GPIO와 레이스터 값을 선언된 내용과, 핀에 할당된 GPIO를 초기화 코드는 아래와 같다.
#define DS1302_CE_GPIO (1)
#define DS1302_DATA_GPIO (12)
#define DS1302_CLK_GPIO (11)
#define DS1302_SEC_WRITE 0x80
#define DS1302_MIN_WRITE 0x82
#define DS1302_HOUR_WRITE 0x84
#define DS1302_DATE_WRITE 0x86
#define DS1302_MONTH_WRITE 0x88
#define DS1302_YEAR_WRITE 0x8C
#define DS1302_WP_WRITE 0x8E
#define DS1302_TRICKLE_WRITE 0x90
#define DS1302_SEC_READ 0x81
#define DS1302_MIN_READ 0x83
#define DS1302_HOUR_READ 0x85
#define DS1302_DATE_READ 0x87
#define DS1302_MONTH_READ 0x89
#define DS1302_YEAR_READ 0x8D
#define DS1302_WP_READ 0x8F
#define DS1302_TRICKLE_READ 0x91
#define WRITE_ENABLE
0x00
#define WRITE_DISABLE
0x80
#define RTC_CLCK_LEN
0x08 /* Size of clock burst */
#define RTC_ADDR_CTRL
0x07 /* Address of control register
*/
#define RTC_ADDR_YEAR
0x06 /* Address of year register */
#define RTC_ADDR_DAY
0x05 /* Address of day of week
register */
#define RTC_ADDR_MON
0x04 /* Address of month register */
#define RTC_ADDR_DATE
0x03 /* Address of day of month
register */
#define RTC_ADDR_HOUR
0x02 /* Address of hour register */
#define RTC_ADDR_MIN
0x01 /* Address of minute register
*/
#define RTC_ADDR_SEC
0x00 /* Address of second register
*/
void DS1302_if_init()
{
gpio_export(DS1302_DATA_GPIO);
gpio_set_outdir(DS1302_DATA_GPIO,1);
gpio_set(DS1302_DATA_GPIO,0);
gpio_export(DS1302_CLK_GPIO);
gpio_set_outdir(DS1302_CLK_GPIO,1);
gpio_set(DS1302_CLK_GPIO,0);
gpio_export(DS1302_CE_GPIO);
gpio_set_outdir(DS1302_CE_GPIO,1);
gpio_set(DS1302_CE_GPIO,0);
}
GPIO를 이용한 한 바이트 데이터 쓰기/읽기 코드는 아래와 같다.
unsigned char DS1302_read_byte()
{
int i = 0;
unsigned int in_level = 0;
unsigned char val = 0;
gpio_set_outdir(DS1302_DATA_GPIO,0);
usleep(2);
for (i=0; i<8; i++)
{
val >>= 1;
in_level =
gpio_get(DS1302_DATA_GPIO);
if (in_level == 1) val |= 0x80;
else val &= 0x7F;
gpio_set(DS1302_CLK_GPIO,1);
usleep(2);
gpio_set(DS1302_CLK_GPIO,0);
usleep(2);
}
return val;
}
void DS1302_write_byte(unsigned char data)
{
int i = 0;
unsigned char val = data;
gpio_set_outdir(DS1302_DATA_GPIO,1);
usleep(2);
for (i=0; i<8; i++)
{
if (val & 1)
gpio_set(DS1302_DATA_GPIO,1);
else gpio_set(DS1302_DATA_GPIO,0);
val >>= 1;
usleep(2);
gpio_set(DS1302_CLK_GPIO,1);
usleep(2);
gpio_set(DS1302_CLK_GPIO,0);
usleep(2);
}
gpio_set(DS1302_DATA_GPIO,0);
}
데이터를 버스트로 읽고/쓰는 코드는 아래와 같다. 위의 한 바이트 데이터를
읽고/쓰는 코드를 사용하여 만들어 졌고, CE핀 조정도 여기에서 한다.
int DS1302_read_bust(unsigned char *buffer,
int len)
{
int i = 0;
if(!buffer) return -1;
gpio_set(DS1302_CE_GPIO,1);
usleep(2);
DS1302_write_byte(0xBF);
for(i=0;i<len;i++)
{
buffer[i] = DS1302_read_byte();
}
gpio_set(DS1302_CE_GPIO,0);
return len;
}
int DS1302_write_bust(unsigned char *buffer,
int len)
{
int i = 0;
if(!buffer) return -1;
gpio_set(DS1302_CE_GPIO,1);
usleep(2);
DS1302_write_byte(DS1302_WP_WRITE);
DS1302_write_byte(WRITE_ENABLE);
DS1302_write_byte(0xBE);
for(i=0;i<len;i++)
{
DS1302_write_byte(buffer[i]);
}
gpio_set(DS1302_CE_GPIO,0);
return len;
}
위의 코드들을 사용해 실제 시간을 쓰고 읽는 코드는 아래와 같다. 시간은
bcd형태의 데이터로 저장된다.
int DS1302_write_time(time_t time_sec)
{
struct tm ltm;
unsigned char buffer[RTC_CLCK_LEN]
= {0,};
if(time_sec == -1) return -1;
if(gmtime_r(&time_sec,<m) == NULL) return -1;
buffer[RTC_ADDR_SEC] =
bin2bcd(ltm.tm_sec); // 0~59
buffer[RTC_ADDR_MIN] =
bin2bcd(ltm.tm_min); // 0~59
buffer[RTC_ADDR_HOUR] =
bin2bcd(ltm.tm_hour); // 0~23
buffer[RTC_ADDR_DATE] =
bin2bcd(ltm.tm_mday); // 1~31
buffer[RTC_ADDR_MON] =
bin2bcd(ltm.tm_mon+1); //
1~12
buffer[RTC_ADDR_DAY] = 0;
buffer[RTC_ADDR_YEAR] =
bin2bcd((ltm.tm_year+1900) % 100); // 0~99
buffer[RTC_ADDR_CTRL] =
WRITE_DISABLE;
DS1302_write_bust(buffer,
RTC_CLCK_LEN);
return 0;
}
time_t DS1302_read_time()
{
struct tm rtc_tm;
unsigned char
buffer[RTC_CLCK_LEN-1] = {0,};
DS1302_read_bust(buffer,RTC_CLCK_LEN-1);
rtc_tm.tm_sec =
(((buffer[RTC_ADDR_SEC] & 0x70) >> 4) * 10) +
(buffer[RTC_ADDR_SEC] & 0x0F);
rtc_tm.tm_min =
(((buffer[RTC_ADDR_MIN] & 0x70) >> 4) * 10) +
(buffer[RTC_ADDR_MIN] & 0x0F);
rtc_tm.tm_hour =
(((buffer[RTC_ADDR_HOUR] & 0x30) >> 4) * 10) +
(buffer[RTC_ADDR_HOUR] & 0x0F);
rtc_tm.tm_mday =
(((buffer[RTC_ADDR_DATE] & 0x30) >> 4) * 10) +
(buffer[RTC_ADDR_DATE] & 0x0F);
rtc_tm.tm_mon =
(((buffer[RTC_ADDR_MON] & 0x10) >> 4) * 10) +
(buffer[RTC_ADDR_MON] & 0x0F) - 1;
rtc_tm.tm_year =
((((buffer[RTC_ADDR_YEAR] & 0xF0) >> 4) * 10) +
(buffer[RTC_ADDR_YEAR] & 0x0F) + 2000) - 1900;
rtc_tm.tm_wday = 0;
rtc_tm.tm_yday = 0;
rtc_tm.tm_isdst = -1;
if ( ((rtc_tm.tm_year + 1900) <
2000) || ((rtc_tm.tm_year + 1900) > 2099)
|| ((rtc_tm.tm_mon +1) < 1) ||
((rtc_tm.tm_mon +1) > 12)
||(rtc_tm.tm_mday < 1) ||
(rtc_tm.tm_mday > 31)
|| (rtc_tm.tm_hour < 0) ||
(rtc_tm.tm_hour > 23)
|| (rtc_tm.tm_min < 0) ||
(rtc_tm.tm_min > 59)
|| (rtc_tm.tm_sec < 0) ||
(rtc_tm.tm_sec > 59) )
{
return -1;
}
return mktime(&rtc_tm);
}
bcd 형태로 변환하는 코드는 아래와 같이 간단하게 구현할 수 있다.
int bin2bcd(int bin)
{
int bcd = 0;
int n = 0;
int count = 0;
int dig = 0;
int num = bin;
for(n = 0; n<4;n++)
{
dig = num %10;
num = num/10;
bcd = (dig<<count) | bcd;
count += 4;
}
return bcd;
}
[관련 포스트]
댓글
댓글 쓰기