summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE24
-rw-r--r--ds1302.c210
-rw-r--r--ds1302.h96
-rw-r--r--examples/burst.c31
-rw-r--r--examples/readwrite.c33
-rw-r--r--makefile20
-rw-r--r--readme14
7 files changed, 428 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cf1ab25
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/ds1302.c b/ds1302.c
new file mode 100644
index 0000000..3d1152b
--- /dev/null
+++ b/ds1302.c
@@ -0,0 +1,210 @@
+#include "ds1302.h"
+#include <stdio.h>
+#include <time.h>
+
+uint8_t ds1302_readAddress(uint8_t address) {
+ return address | 0x01;
+}
+
+void ds1302_spi_session_start(struct ds1302_spi_session * session) {
+ /*Setup Pins*/
+ bcm2835_gpio_fsel(session->clock, BCM2835_GPIO_FSEL_OUTP);
+ bcm2835_gpio_fsel(session->select, BCM2835_GPIO_FSEL_OUTP);
+ bcm2835_gpio_fsel(session->io, BCM2835_GPIO_FSEL_OUTP);
+
+ bcm2835_gpio_write(session->clock, LOW);
+ bcm2835_delayMicroseconds(10);
+ bcm2835_gpio_write(session->select, HIGH);
+ bcm2835_gpio_write(session->io, LOW);
+
+ bcm2835_delayMicroseconds(10);
+}
+
+void ds1302_spi_session_end(struct ds1302_spi_session * session) {
+ bcm2835_gpio_write(session->select, LOW);
+ bcm2835_delayMicroseconds(10);
+}
+
+void ds1302_spi_session_write(struct ds1302_spi_session * session, int8_t value) {
+ bcm2835_gpio_fsel(session->io, BCM2835_GPIO_FSEL_OUTP);
+
+ for(int i = 0; i < 8; i++) {
+ /*The LSB is written first*/
+ uint8_t out = value & 0x01;
+ value = value >> 1;
+
+ uint8_t o;
+ if(out != 0x0)
+ o = HIGH;
+ else
+ o = LOW;
+
+ /*ds1302 reads the value while the clock is high. Ensure that value is written*/
+ bcm2835_gpio_write(session->io, o);
+ bcm2835_delayMicroseconds(DS1302_CLOCKDELAY);
+ bcm2835_gpio_write(session->clock, HIGH);
+ /*TODO: Lookup the required wait time*/
+ bcm2835_delayMicroseconds(DS1302_CLOCKDELAY);
+ bcm2835_gpio_write(session->clock, LOW);
+ }
+}
+
+uint8_t ds1302_spi_session_read(struct ds1302_spi_session * session) {
+ bcm2835_gpio_fsel(session->io, BCM2835_GPIO_FSEL_INPT);
+ uint8_t value = 0;
+
+ /*The LSB is read first*/
+ for(int i = 0; i < 8; i++) {
+ value = value >> 1 | bcm2835_gpio_lev(session->io) << 7;
+
+ bcm2835_gpio_write(session->clock, HIGH);
+ bcm2835_delayMicroseconds(DS1302_CLOCKDELAY);
+ bcm2835_gpio_write(session->clock, LOW);
+ //bcm2835_delayMicroseconds(DS1302_CLOCKDELAY);
+ }
+
+ return value;
+}
+
+uint8_t ds1302_set_writeprotect(struct ds1302_spi_session * session, uint8_t protect) {
+ if(protect != 0x0)
+ protect = 0x80;
+
+ ds1302_spi_session_write(session, DS1302_WRITE_PROTECT);
+ ds1302_spi_session_write(session, protect);
+}
+
+uint8_t ds1302_read_register(struct ds1302_spi_session * session, uint8_t address) {
+ ds1302_spi_session_write(session, ds1302_readAddress(address));
+ return ds1302_spi_session_read(session);
+}
+
+void ds1302_write_register(struct ds1302_spi_session * session, uint8_t address, uint8_t data) {
+ ds1302_spi_session_write(session, address);
+ ds1302_spi_session_write(session, data);
+}
+
+uint8_t ds1302_burst_read(struct ds1302_spi_session * session, ds1302BurstType type, uint8_t * output, size_t size) {
+ if(size <= 0)
+ return 0;
+
+ uint8_t command = ds1302_readAddress(DS1302_CLOCK_BURST);
+ uint8_t bytesRead = 0;
+
+ if(type == DS1302_MEMORY)
+ command = command | 0x40;
+
+ ds1302_spi_session_write(session, command);
+ /*read more about burst reads from memory*/
+ for(int i = 0; i < size && i < 9; i++) {
+ output[i] = ds1302_spi_session_read(session);
+ bytesRead++;
+ }
+
+ /*Pull the select line down to signal that we are done reading*/
+ bcm2835_gpio_write(session->select, LOW);
+ bcm2835_delayMicroseconds(10);
+ bcm2835_gpio_write(session->select, HIGH);
+
+ return bytesRead;
+}
+uint8_t ds1302_burst_write(struct ds1302_spi_session * session, ds1302BurstType type, uint8_t * input, size_t size) {
+ if(size <= 0)
+ return 0;
+
+ uint8_t command = DS1302_CLOCK_BURST;
+ uint8_t bytesWritten = 0;
+
+ if(type == DS1302_MEMORY)
+ command = command | 0x40;
+
+ ds1302_spi_session_write(session, command);
+
+ for(int i = 0; i < size; i++) {
+ ds1302_spi_session_write(session, input[i]);
+ bytesWritten++;
+ }
+
+ /*Pull the select line down to signal that we are done writing*/
+ bcm2835_gpio_write(session->select, LOW);
+ bcm2835_delayMicroseconds(10);
+ bcm2835_gpio_write(session->select, HIGH);
+
+ return bytesWritten;
+}
+
+/*Convert the base 10 hex representation to just base 10
+ The upper half represents the tens digit and the lower half
+ represents the ones digit.
+ Make sure to clear any bits that are not used in the representation.
+ (EG the 12/24 and AM/PM bits for the hour register)
+*/
+uint8_t ds1302_convertToDecimal(uint8_t input) {
+ uint8_t result = 0x0;
+ result = ((input >> 4) * 10) + (input & 0x0F);
+ return result;
+}
+
+/*Convert base 10 to the 10 hex representation
+ Inverse of ds1302_convertToDecimal
+*/
+uint8_t ds1302_convertToDecimalHex(uint8_t input) {
+ uint8_t result = 0x0;
+ uint8_t tens = input / 10;
+ uint8_t ones = input - (tens * 10);
+ result = (tens << 4) | ones;
+ return result;
+}
+
+void ds1302_getCalendarTime(struct ds1302_spi_session * session, struct tm * date) {
+ uint8_t data[7] = {0xff};
+ /*Clock hour format: High: 12hr, Low: 24h*/
+ uint8_t h12;
+ /*PM or AM: High: PM, Low: AM*/
+ uint8_t pm;
+ uint8_t hour;
+
+ ds1302_burst_read(session, DS1302_REGISTER, data, 7);
+
+ date->tm_sec = ds1302_convertToDecimal(data[0] & (~0x80));
+ date->tm_min = ds1302_convertToDecimal(data[1] & (~0x80));
+
+ /*struct tm represents hours in 24 format. Convert the 12hr clock to 24hr*/
+ h12 = data[2] & 0x80;
+ pm = data[2] & 0x20;
+ hour = ds1302_convertToDecimal(data[2] & 0x1F);
+ if(h12) {
+ if(pm) {
+ date->tm_hour = hour + 11;
+ } else {
+ date->tm_hour = hour - 1;
+ }
+ } else {
+ date->tm_hour = hour;
+ }
+
+ date->tm_mday = ds1302_convertToDecimal(data[3]);
+ /*struct tm starts counting from 0 whereas the ds1302 starts from 1*/
+ date->tm_mon = ds1302_convertToDecimal(data[4]) - 1;
+ /*struct tm starts counting from 0 whereas the ds1302 starts from 1*/
+ date->tm_wday = ds1302_convertToDecimal(data[5]) - 1;
+ /*Number of year that have passed since 1900*/
+ date->tm_year = (ds1302_convertToDecimal(data[6]) + DS1302_START_YEAR) - 1900;
+}
+
+void ds1302_setCalendarTime(struct ds1302_spi_session * session, struct tm * date) {
+ /*TODO: disable Write protect*/
+ uint8_t data[8] = {0};
+ uint8_t hour = 0;
+
+ data[0] = ds1302_convertToDecimalHex(date->tm_sec);
+ data[1] = ds1302_convertToDecimalHex(date->tm_min);
+ data[2] = ds1302_convertToDecimalHex(date->tm_hour);
+ data[3] = ds1302_convertToDecimalHex(date->tm_mday);
+ data[4] = ds1302_convertToDecimalHex(date->tm_mon + 1);
+ data[5] = ds1302_convertToDecimalHex(date->tm_wday + 1);
+ data[6] = ds1302_convertToDecimalHex((date->tm_year + 1900) - DS1302_START_YEAR);
+ data[7] = 0x00;
+
+ ds1302_burst_write(session, DS1302_REGISTER, data, 8);
+}
diff --git a/ds1302.h b/ds1302.h
new file mode 100644
index 0000000..d381265
--- /dev/null
+++ b/ds1302.h
@@ -0,0 +1,96 @@
+#include <bcm2835.h>
+#include <time.h>
+
+/*Registers Addresses*/
+#define DS1302_SECOND 0x80
+#define DS1302_MINUTE 0x82
+#define DS1302_HOUR 0x84
+#define DS1302_DATE 0x86
+#define DS1302_MONTH 0x88
+#define DS1302_DAY 0x8A
+#define DS1302_YEAR 0x8C
+#define DS1302_WRITE_PROTECT 0x8E
+#define DS1302_TRICKLE_CHARGER 0x90
+#define DS1302_CLOCK_BURST 0xBE
+#define DS1302_RAM 0xC0
+
+#define DS1302_CLOCKDELAY 15
+
+#define DS1302_START_YEAR 2000
+
+/*! Clock refers to the CLK pin
+ select refers to CE pin or RST pin on older devices
+ io refers to the input/Push-Pull Output pin
+*/
+struct ds1302_spi_session {
+ RPiGPIOPin clock;
+ RPiGPIOPin select;
+ RPiGPIOPin io;
+};
+
+/*! Where a burst operation is reading from or writing to. */
+typedef enum {
+ DS1302_REGISTER,
+ DS1302_MEMORY
+} ds1302BurstType;
+
+typedef enum {
+ DS1302_SUNDAY = 1,
+ DS1302_MONDAY,
+ DS1302_TUESDAY,
+ DS1302_WEDNESDAY,
+ DS1302_THURSDAY,
+ DS1302_FRIDAY,
+ DS1302_SATURDAY
+} ds1302WeekDay;
+
+
+
+/*! Initialize the SPI connection over the GPIO pins specified in ds1302_spi_session.
+ This will set the CLK and I/O lines low, and the CE pin will be set high
+*/
+void ds1302_spi_session_start(struct ds1302_spi_session * session);
+
+/*! Terminate the SPI connection
+ This will set the CLK and CE pins low.
+*/
+void ds1302_spi_session_end(struct ds1302_spi_session * session);
+
+/*! Read a register on the ds1302. The result is returned*/
+uint8_t ds1302_read_register(struct ds1302_spi_session * session, uint8_t address);
+/*! Write to register on the ds1302.*/
+void ds1302_write_register(struct ds1302_spi_session * session, uint8_t address, uint8_t data);
+
+/*! Perform a burst read operation on either the clock registers or the memory registers.
+ output will contain the results and size must be the allocated size of output.
+ Returns the number of bytes read.
+*/
+uint8_t ds1302_burst_read(struct ds1302_spi_session * session, ds1302BurstType type, uint8_t * output, size_t size);
+
+/*! Perform a burst write operation on either the clock registers or the memory registers.
+ Returns the number of bytes written.
+*/
+uint8_t ds1302_burst_write(struct ds1302_spi_session * session, ds1302BurstType type, uint8_t * input, size_t size);
+
+/*! Obtain the read address for a register */
+uint8_t ds1302_readAddress(uint8_t address);
+
+
+/*! Set the write protect register */
+uint8_t ds1302_set_writeprotect(struct ds1302_spi_session * session, uint8_t protect);
+
+/*! Convert the encoding on the ds1302 obtained from the ds1302 to regular base 10 encoding. */
+uint8_t ds1302_convertToDecimal(uint8_t input);
+
+/*! Inverse of ds1302_convertToDecimal. Converts base 10 encoding to the ds1302's representation. */
+uint8_t ds1302_convertToDecimalHex(uint8_t input);
+
+/*! Perform a burst read operation on the clock registers and return the date
+ inside a struct tm.
+*/
+void ds1302_getCalendarTime(struct ds1302_spi_session * session, struct tm * date);
+
+/*! Perform a burst write operation on the clock registers and set the date
+ according to the provided struct tm.
+*/
+void ds1302_setCalendarTime(struct ds1302_spi_session * session, struct tm * date);
diff --git a/examples/burst.c b/examples/burst.c
new file mode 100644
index 0000000..e8c166e
--- /dev/null
+++ b/examples/burst.c
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include "ds1302.h"
+
+int main(int argc, char ** argv) {
+ if(!bcm2835_init()) {
+ printf("Unable to init bcm2835\n");
+ return 0;
+ }
+
+ struct tm *now, rtc;
+ time_t rawTime;
+ time(&rawTime);
+ struct ds1302_spi_session session = {RPI_GPIO_P1_23, RPI_GPIO_P1_24, RPI_GPIO_P1_21 };
+
+ ds1302_spi_session_start(&session);
+
+ ds1302_getCalendarTime(&session, &rtc);
+ printf("DS1302 Time:\t %s", asctime(&rtc));
+
+ now = gmtime(&rawTime);
+ printf("Host Time:\t %s", asctime(now));
+
+ printf("Setting ds1302 time to host time\n");
+ ds1302_setCalendarTime(&session, now);
+
+ ds1302_getCalendarTime(&session, &rtc);
+ printf("DS1302 Time:\t %s", asctime(&rtc));
+
+ ds1302_spi_session_end(&session);
+ return 0;
+}
diff --git a/examples/readwrite.c b/examples/readwrite.c
new file mode 100644
index 0000000..4b48bd6
--- /dev/null
+++ b/examples/readwrite.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include "ds1302.h"
+
+int main(int argc, char ** argv) {
+
+ uint8_t seconds = 0x00;
+ uint8_t minute = 0x00;
+ uint8_t writeProtect = 0xFF;
+ uint8_t buffer[9];
+ struct tm date;
+
+ if(!bcm2835_init()) {
+ printf("Unable to init bcm2835\n");
+ return 0;
+ }
+
+ struct ds1302_spi_session session = {RPI_GPIO_P1_23, RPI_GPIO_P1_24, RPI_GPIO_P1_21 };
+ ds1302_spi_session_start(&session);
+ ds1302_set_writeprotect(&session, 0x00);
+
+ seconds = ds1302_read_register(&session, DS1302_SECOND);
+ minute = ds1302_read_register(&session, DS1302_MINUTE);
+ printf("Seconds: 0x%X minute: 0x%X\n", seconds, minute);
+
+ delay(1000);
+
+ seconds = ds1302_read_register(&session, DS1302_SECOND);
+ minute = ds1302_read_register(&session, DS1302_MINUTE);
+ printf("Seconds: 0x%X minute: 0x%X\n", seconds, minute);
+
+ ds1302_spi_session_end(&session);
+ return 0;
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..ee700e1
--- /dev/null
+++ b/makefile
@@ -0,0 +1,20 @@
+CC=gcc
+CFLAGS=-std=c99 -I.
+
+all: example
+
+example: burstExample rwExample
+
+rwExample: ds1302
+ $(CC) $(CFLAGS) examples/readwrite.c ds1302.o -l bcm2835 -o rwExample
+
+burstExample: ds1302
+ $(CC) $(CFLAGS) examples/burst.c ds1302.o -l bcm2835 -o burstExample
+
+ds1302:
+ $(CC) $(CFLAGS) -c ds1302.c
+
+clean: .PHONY
+ rm *.o burstExample rwExample
+
+.PHONY:
diff --git a/readme b/readme
new file mode 100644
index 0000000..7fd78e6
--- /dev/null
+++ b/readme
@@ -0,0 +1,14 @@
+# DS1302 Library for the Raspberry Pi
+
+This is a library for interacting with the serial interface of a DS1302 with a Raspberry Pi.
+Serial communications is bit banged instead of using the Pi's SPI pins to allow flexibility when setting up.
+Single register read and write operations are supported and burst read and write operations on clock and ram registers are also supported.
+Additional utilities for manipulating `struct tm` on the device is also provided.
+
+## Requirements
+bcm2835 library <http://www.airspayce.com/mikem/bcm2835/index.html>
+
+## References
+
+DS1302 Datasheet <https://datasheets.maximintegrated.com/en/ds/DS1302.pdf>
+bcm2835 arm peripherals <https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf>