Модуль часов реального времени (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

results matching ""

    No results matching ""