From ee820891c9f9c7db8aa5ea2ae24a71e1a3c16b31 Mon Sep 17 00:00:00 2001 From: David Blajda Date: Tue, 17 Oct 2017 19:38:10 -0400 Subject: Initailize repository --- LICENSE | 24 ++++++ ds1302.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++ ds1302.h | 96 +++++++++++++++++++++++ examples/burst.c | 31 ++++++++ examples/readwrite.c | 33 ++++++++ makefile | 20 +++++ readme | 14 ++++ 7 files changed, 428 insertions(+) create mode 100644 LICENSE create mode 100644 ds1302.c create mode 100644 ds1302.h create mode 100644 examples/burst.c create mode 100644 examples/readwrite.c create mode 100644 makefile create mode 100644 readme 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 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 +#include + +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 +#include + +/*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 +#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 +#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 + +## References + +DS1302 Datasheet +bcm2835 arm peripherals -- cgit v1.2.3