Модуль часов реального времени (DS1302 real time clock module)
Модуль часов выполнен на основе чипа ds-1302. Часы позволяют считать секунды, минуты, часы, день недели, день месяца, месяц, год с учетом высокосных лет до 2100 года. Есть возможность вести 12 или 24 часовой учет времени.
Подключение
Код
Создайте файл rtc-pi.c с кодом:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/time.h>
#define GPIO_ADD 0x20200000L // physical address of I/O peripherals on the ARM processor
#define GPIO_SEL1 1 // Offset of SEL register for GP17 & GP18 into GPIO bank
#define GPIO_SEL2 2 // Offset of SEL register for GP21 into GPIO bank
#define GPIO_SET 7 // Offset of PIN HIGH register into GPIO bank
#define GPIO_CLR 10 // Offset of PIN LOW register into GPIO bank
#define GPIO_INP 13 // Offset of PIN INPUT value register into GPIO bank
#define PAGE_SIZE 4096
#define BLOCK_SIZE PAGE_SIZE
/* RTC Chip register definitions */
#define SEC_WRITE 0x80
#define MIN_WRITE 0x82
#define HOUR_WRITE 0x84
#define DATE_WRITE 0x86
#define MONTH_WRITE 0x88
#define YEAR_WRITE 0x8C
#define SEC_READ 0x81
#define MIN_READ 0x83
#define HOUR_READ 0x85
#define DATE_READ 0x87
#define MONTH_READ 0x89
#define YEAR_READ 0x8D
int mem_fd = 0;
char *gpio_mmap = NULL;
char *gpio_ram = NULL;
volatile unsigned int *gpio = NULL;
/* These 'defines' map the peripheral pin functions to our circuits DS1302 pins */
/* See DS1302 datasheet REV: 110805, and Broadcom BCM2835-ARM-Peripherals.pdf 6/2/2012 */
#define IO_INPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL
#define IO_OUTPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL; *(gpio+GPIO_SEL1) |= 0x01000000L
/* RPi v1:
#define SCLK_OUTPUT *(gpio+GPIO_SEL2) &= 0xFFFFFFC7L; *(gpio+GPIO_SEL2) |= 0x00000008L
RPi v2: */
#define SCLK_OUTPUT *(gpio+GPIO_SEL2) &= 0xFF1FFFFFL; *(gpio+GPIO_SEL2) |= 0x00200000L
#define CE_OUTPUT *(gpio+GPIO_SEL1) &= 0xFF1FFFFFL; *(gpio+GPIO_SEL1) |= 0x00200000L
#define IO_HIGH *(gpio+GPIO_SET) = 0x00040000L
#define IO_LOW *(gpio+GPIO_CLR) = 0x00040000L
/* RPi v1:
#define SCLK_HIGH *(gpio+GPIO_SET) = 0x00200000L
#define SCLK_LOW *(gpio+GPIO_CLR) = 0x00200000L
RPi v2: */
#define SCLK_HIGH *(gpio+GPIO_SET) = 0x08000000L
#define SCLK_LOW *(gpio+GPIO_CLR) = 0x08000000L
#define CE_HIGH *(gpio+GPIO_SET) = 0x00020000L
#define CE_LOW *(gpio+GPIO_CLR) = 0x00020000L
#define IO_LEVEL *(gpio+GPIO_INP) & 0x00040000L
void setup_io()
{
/* open /dev/mem to get acess to physical ram */
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
printf("can't open /dev/mem. Did you run the program with administrator rights?\n");
exit (-1);
}
/* Allocate a block of virtual RAM in our application's address space */
if ((gpio_ram = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
printf("allocation error \n");
exit (-1);
}
/* Make sure the pointer is on 4K boundary */
if ((unsigned long)gpio_ram % PAGE_SIZE)
gpio_ram += PAGE_SIZE - ((unsigned long)gpio_ram % PAGE_SIZE);
/* Now map the physical addresses of the peripheral control registers
into our address space */
gpio_mmap = (unsigned char *)mmap(
(caddr_t)gpio_ram,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED,
mem_fd,
GPIO_ADD
);
if ((long)gpio_mmap < 0) {
printf("unable to map the memory. Did you run the program with administrator rights?\n");
exit (-1);
}
/* Always use a volatile pointer to hardware registers */
gpio = (volatile unsigned *)gpio_mmap;
/* Now we have access to the hardware reigsters we can start twiddling I/O pins */
/* Switch GPIO 0, 1 and 2 to output mode */
SCLK_OUTPUT;
IO_OUTPUT;
CE_OUTPUT;
/* Set the SCLK, IO and CE pins to default (low) */
SCLK_LOW;
IO_LOW;
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
}
unsigned char read_rtc( unsigned char add )
{
unsigned char val;
int lp;
val = add;
/* Check LSB is set */
if ( !(add & 1 ) ) {
printf("Incorrect read address specified - LSB must be set.\n");
exit (-1);
}
/* Check address range is valid */
if ( (add < 0x81) || (add > 0x91) ) {
printf("Incorrect read address specified - It must be in the range 0x81..0x91\n");
exit (-1);
}
CE_HIGH;
usleep(2);
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
IO_INPUT;
for (lp=0; lp<8; lp++) {
usleep(2);
val >>= 1;
if (IO_LEVEL)
val |= 0x80;
else
val &= 0x7F;
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
/* Set the I/O pin back to it's default, output low. */
IO_LOW;
IO_OUTPUT;
/* Set the CE pin back to it's default, low */
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
return val;
}
void write_rtc( unsigned char add, unsigned char val_to_write )
{
unsigned char val;
int lp;
/* Check LSB is clear */
if ( add & 1 ) {
printf("Incorrect write address specified - LSB must be cleared.\n");
exit (-1);
}
/* Check address range is valid */
if ( (add < 0x80) || (add > 0x90) ) {
printf("Incorrect write address specified - It must be in the range 0x80..0x90\n");
exit (-1);
}
CE_HIGH;
usleep(2);
val = add;
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
val = val_to_write;
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
/* Set the I/O pin back to it's default, output low. */
IO_LOW;
/* Set the CE pin back to it's default, low */
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
}
int main(int argc, char **argv)
{
int lp;
unsigned char val;
int year,month,day,hour,minute,second;
time_t epoch_time;
struct tm time_requested;
struct timeval time_setformat;
/* Check that the program was called correctly */
if ( argc > 2 ) {
printf("Too many arguments specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n");
exit (-1);
}
/* Set up gpi pointer for direct register access */
setup_io();
if ( argc == 2 ) {
/* If the number of arguments are two, that means the user enter a date & time. */
/* Read that value and write it to the RTC chip */
sscanf(argv[1],"%4d%2d%2d%2d%2d%2d",&year,&month,&day,&hour,&minute,&second);
/* Validate that the input date and time is basically sensible */
if ( (year < 2000) || (year > 2099) || (month < 1) || (month > 12) ||
(day < 1) || (day>31) || (hour < 0) || (hour > 23) || (minute < 0) ||
(minute > 59) || (second < 0) || (second > 59) ) {
printf("Incorrect date and time specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n");
exit (-1);
}
/* Got valid input - now write it to the RTC */
/* The RTC expects the values to be written in packed BCD format */
write_rtc(SEC_WRITE, ( (second/10) << 4) | ( second % 10) );
write_rtc(MIN_WRITE, ( (minute/10) << 4) | ( minute % 10) );
write_rtc(HOUR_WRITE, ( (hour/10) << 4) | ( hour % 10) );
write_rtc(DATE_WRITE, ( (day/10) << 4) | ( day % 10) );
write_rtc(MONTH_WRITE, ( (month/10) << 4) | ( month % 10) );
write_rtc(YEAR_WRITE, ( ((year-2000)/10) << 4) | (year % 10) );
/* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */
time_requested.tm_sec = second;
time_requested.tm_min = minute;
time_requested.tm_hour = hour;
time_requested.tm_mday = day;
time_requested.tm_mon = month-1;
time_requested.tm_year = year-1900;
time_requested.tm_wday = 0; /* not used */
time_requested.tm_yday = 0; /* not used */
time_requested.tm_isdst = -1; /* determine daylight saving time from the system */
epoch_time = mktime(&time_requested);
/* Now set the clock to this time */
time_setformat.tv_sec = epoch_time;
time_setformat.tv_usec = 0;
printf("Set UNIX timestamp to RTC: %lld\n", (long long) epoch_time );
lp = settimeofday(&time_setformat,NULL);
/* Check that the change was successful */
if ( lp < 0 ) {
printf("Unable to change the system time. Did you run the program as an administrator?\n");
printf("The operation returned the error message \"%s\"\n", strerror( errno ) );
exit (-1);
}
} else {
/* The program was called without a date specified; therefore read the date and time from */
/* the RTC chip and set the system time to this */
second = read_rtc(SEC_READ);
minute = read_rtc(MIN_READ);
hour = read_rtc(HOUR_READ);
day = read_rtc(DATE_READ);
month = read_rtc(MONTH_READ);
year = read_rtc(YEAR_READ);
/* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */
/* Bearing in mind that the format of the time values in the RTC is packed BCD, hence the conversions */
time_requested.tm_sec = (((second & 0x70) >> 4) * 10) + (second & 0x0F);
time_requested.tm_min = (((minute & 0x70) >> 4) * 10) + (minute & 0x0F);
time_requested.tm_hour = (((hour & 0x30) >> 4) * 10) + (hour & 0x0F);
time_requested.tm_mday = (((day & 0x30) >> 4) * 10) + (day & 0x0F);
time_requested.tm_mon = (((month & 0x10) >> 4) * 10) + (month & 0x0F) - 1;
time_requested.tm_year = (((year & 0xF0) >> 4) * 10) + (year & 0x0F) + 2000 - 1900;
time_requested.tm_wday = 0; /* not used */
time_requested.tm_yday = 0; /* not used */
time_requested.tm_isdst = -1; /* determine daylight saving time from the system */
epoch_time = mktime(&time_requested);
/* Now set the clock to this time */
time_setformat.tv_sec = epoch_time;
time_setformat.tv_usec = 0;
printf("Read UNIX timestamp from RTC: %lld\n", (long long) epoch_time );
lp = settimeofday(&time_setformat,NULL);
/* Check that the change was successful */
if ( lp < 0 ) {
printf("Unable to change the system time. Did you run the program as an administrator?\n");
printf("The operation returned the error message \"%s\"\n", strerror( errno ) );
exit (-1);
}
}
return 0;
}
Запустите код
cc rtc-pi.c -o rtc-pi
Установите время:
sudo rtc-pi YYYYMMDDhhmmss
Читайте время с модуля:
sudo rtc-pi