diff --git a/Images/how_to_build/1_select_an_example.png b/Images/how_to_build/1_select_an_example.png new file mode 100644 index 0000000..c8377bb Binary files /dev/null and b/Images/how_to_build/1_select_an_example.png differ diff --git a/Images/how_to_build/2_select_port.png b/Images/how_to_build/2_select_port.png new file mode 100644 index 0000000..c212c6c Binary files /dev/null and b/Images/how_to_build/2_select_port.png differ diff --git a/Images/how_to_build/3_build_and_load_image.png b/Images/how_to_build/3_build_and_load_image.png new file mode 100644 index 0000000..bf6e779 Binary files /dev/null and b/Images/how_to_build/3_build_and_load_image.png differ diff --git a/Images/how_to_build/4_after_load_output_screen.png b/Images/how_to_build/4_after_load_output_screen.png new file mode 100644 index 0000000..ce65091 Binary files /dev/null and b/Images/how_to_build/4_after_load_output_screen.png differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..da1f5e7 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Analog RTC Lib + +AnalogRTCLib package includes arduino driver and usecase examples for MAXIM/ADI RTCs. +Supported part numbers: + +- [MAX31331](https://www.maximintegrated.com/en/products/analog/MAX31331.html) +- [MAX31334](https://www.maximintegrated.com/en/products/analog/MAX31334.html) +- [MAX31328](https://www.maximintegrated.com/en/products/analog/real-time-clocks/MAX31328.html) +- [MAX31341](https://www.maximintegrated.com/en/products/analog/real-time-clocks/MAX31341B.html) +- [MAX31343](https://www.maximintegrated.com/en/products/analog/real-time-clocks/MAX31343.html) + +## How to install +There are two main options to install library: +### Option 1: + 1. Open Arduino IDE + 2. Go into Tools -> Manage Libraries... + 3. Search for AnalogRTCLib + 4. Click install button + +### Option 2: + 1. Dowload repository as .zip file + 2. Rename .zip file as "AnalogRTCLib.zip" + 3. Open Arduino IDE + 4. Go into Sketch -> Include Library -> Add .ZIP Library... + 5. Browse the AnalogRTCLib.zip location + 6. Click Open + + ## How to build and load an example + 1. After installation open Arduino IDE + 2. Go into Files -> Examples -> AnalogRTCLib + 3. Select the part number you would like to use + 4. Select an example + 5. (If needs) Update example pin connection in example to it match with your target board. + + ![Select an example](./Images/how_to_build/1_select_an_example.png) + + 6. Plug your Arduino board to PC via USB cable. + 7. Select board type and serial port by navigating to + Tools->Board + Tools->Ports + + ![Select board and port](./Images/how_to_build/2_select_port.png) + + 8. Click right arrow button to build and load it + + ![Build and load image](./Images/how_to_build/3_build_and_load_image.png) + + 9. Please check output to see whether load succeeded or not + + ![Output screen](./Images/how_to_build/4_after_load_output_screen.png) \ No newline at end of file diff --git a/examples/MAX31328/Alarm1/Alarm1.ino b/examples/MAX31328/Alarm1/Alarm1.ino new file mode 100644 index 0000000..c9d4d23 --- /dev/null +++ b/examples/MAX31328/Alarm1/Alarm1.ino @@ -0,0 +1,78 @@ +#include + +MAX31328 rtc(&Wire, MAX3128_I2C_ADDRESS); + +// Pin Number that connects to MAX31328 interrupt pin +// Please update pin_inta depend on your target board connection +int pin_inta = 2; + +char time_char_buffer[40]; +struct tm rtc_ctime; + +void print_time(void) { + + rtc.get_time(&rtc_ctime); + + strftime(time_char_buffer, sizeof(time_char_buffer), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(time_char_buffer); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("MAX31328 ALARM1 use case example:"); + Serial.println("MAX31328 will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous sensor configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_inta, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.set_alarm(MAX31328::ALARM1, &rtc_ctime, MAX31328::ALARM_PERIOD_EVERYSECOND); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31328::INTR_ID_ALARM1); + if (ret) { + Serial.println("IRQ enable failed!"); + } + + // clear all irq flags on startup + rtc.irq_clear_flag(); + + print_time(); // print current time +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_inta); + + if (pin_state == LOW) { + print_time(); + rtc.irq_clear_flag(MAX31328::INTR_ID_ALARM1); + } +} diff --git a/examples/MAX31328/Alarm2/Alarm2.ino b/examples/MAX31328/Alarm2/Alarm2.ino new file mode 100644 index 0000000..affb549 --- /dev/null +++ b/examples/MAX31328/Alarm2/Alarm2.ino @@ -0,0 +1,78 @@ +#include + +MAX31328 rtc(&Wire, MAX3128_I2C_ADDRESS); + +// Pin Number that connects to MAX31328 interrupt pin +// Please update pin_inta depend on your target board connection +int pin_inta = 2; + +char time_char_buffer[40]; +struct tm rtc_ctime; + +void print_time(void) { + + rtc.get_time(&rtc_ctime); + + strftime(time_char_buffer, sizeof(time_char_buffer), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(time_char_buffer); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("MAX31328 ALARM2 use case example:"); + Serial.println("MAX31328 will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous sensor configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_inta, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.set_alarm(MAX31328::ALARM2, &rtc_ctime, MAX31328::ALARM_PERIOD_EVERYMINUTE); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31328::INTR_ID_ALARM2); + if (ret) { + Serial.println("IRQ enable failed!"); + } + + // clear all irq flags on startup + rtc.irq_clear_flag(); + + print_time(); // print current time +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_inta); + + if (pin_state == LOW) { + print_time(); + rtc.irq_clear_flag(MAX31328::INTR_ID_ALARM2); + } +} diff --git a/examples/MAX31328/Temperature/Temperature.ino b/examples/MAX31328/Temperature/Temperature.ino new file mode 100644 index 0000000..dfe18b5 --- /dev/null +++ b/examples/MAX31328/Temperature/Temperature.ino @@ -0,0 +1,56 @@ +#include + +MAX31328 rtc(&Wire, MAX3128_I2C_ADDRESS); + +#define MAX_LOOP_COUNTER 250 + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("RTC temperature use case example:"); + Serial.println("Temperature will be read periodically"); + Serial.println(" "); + + rtc.begin(); +} + +void loop() { + int ret; + + delay(500); // wait a little + + ret = rtc.start_temp_conversion(); + if (ret) { + Serial.println("Start temperature converstion failed!"); + return; + } + + int counter = MAX_LOOP_COUNTER; // max wait time + do { + delay(1); + if (rtc.is_temp_ready() == 0) { // returns 0 on success + break; // means converstion done, so break + } + } while(--counter); + + if (counter == 0) { + Serial.println("Timeout occur!"); + } else { + float temp = 0; + ret = rtc.get_temp(temp); + if (ret) { + Serial.println("Temperature read failed!"); + } else { + Serial.print("Conversion time: "); + Serial.print( MAX_LOOP_COUNTER - counter ); + Serial.print(" ms"); + + Serial.print(" "); + Serial.print("Temperature: "); + Serial.print(temp, 2); + Serial.println(" Celsius"); + } + } +} diff --git a/examples/MAX31328/Time/Time.ino b/examples/MAX31328/Time/Time.ino new file mode 100644 index 0000000..b63273e --- /dev/null +++ b/examples/MAX31328/Time/Time.ino @@ -0,0 +1,48 @@ +#include + +MAX31328 rtc(&Wire, MAX3128_I2C_ADDRESS); + +char time_char_buffer[40]; +struct tm rtc_ctime; + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("Set get rtc use case example:"); + Serial.println("RTC will be set to specific value then it will be read every second"); + Serial.println(" "); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } +} + +void loop() { + int ret; + + delay(1000); // wait a little + + ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + } else { + strftime(time_char_buffer, 40, "%F %T", &rtc_ctime); + Serial.println(time_char_buffer); + } +} diff --git a/examples/MAX3133X/Alarm/Alarm.ino b/examples/MAX3133X/Alarm/Alarm.ino new file mode 100644 index 0000000..f865fa4 --- /dev/null +++ b/examples/MAX3133X/Alarm/Alarm.ino @@ -0,0 +1,237 @@ +#include + +MAX31331 *rtc; + +int INTAb = PIN2; +int INTBb = PIN3; +volatile bool interrupt_occured = false; + +const char *days[] = {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}; + +void set_test_time(tm * testTime) +{ + /*Date: 31/12/2022 Day: Saturday + Time: 23:59:45*/ + testTime->tm_sec = 45; + testTime->tm_min = 59; + testTime->tm_hour = 23; + testTime->tm_mday = 31; + testTime->tm_mon = 11; + testTime->tm_year = 122; + testTime->tm_wday = 5; + testTime->tm_yday = 0; + testTime->tm_isdst = 0; +} + +void print_time(tm *rtc_ctime, uint16_t *sub_sec = NULL) { + Serial.print("Date: "); + Serial.print(rtc_ctime->tm_mday); + Serial.print("/"); + Serial.print(rtc_ctime->tm_mon + 1); + Serial.print("/"); + Serial.print(rtc_ctime->tm_year + 1900); + Serial.print(" Day: "); + Serial.println(days[rtc_ctime->tm_wday]); + + Serial.print("Time: "); + Serial.print(rtc_ctime->tm_hour); + Serial.print(":"); + Serial.print(rtc_ctime->tm_min); + Serial.print(":"); + if(sub_sec == NULL) { + Serial.println(rtc_ctime->tm_sec); + } else { + Serial.print(rtc_ctime->tm_sec); + Serial.print("."); + Serial.println(*sub_sec); + } +} + +void print_period(MAX3133X::alarm_period_t period) { + (period == MAX3133X::ALARM_PERIOD_EVERYSECOND) ? Serial.println("Once a second") : \ + (period == MAX3133X::ALARM_PERIOD_EVERYMINUTE) ? Serial.println("Seconds match") : \ + (period == MAX3133X::ALARM_PERIOD_HOURLY) ? Serial.println("Seconds, Minutes match") : \ + (period == MAX3133X::ALARM_PERIOD_DAILY) ? Serial.println("Hours, Minutes, Seconds match") : \ + (period == MAX3133X::ALARM_PERIOD_WEEKLY) ? Serial.println("Day, Time match") : \ + (period == MAX3133X::ALARM_PERIOD_MONTHLY) ? Serial.println("Date, Time match") : \ + (period == MAX3133X::ALARM_PERIOD_YEARLY) ? Serial.println("Month, Date, Time match") : \ + (period == MAX3133X::ALARM_PERIOD_ONETIME) ? Serial.println("Year, Month, Date, Time match") : \ + Serial.println("Invalid!"); +} + +void rtc_interrupt_handler() { + interrupt_occured = true; +} + +void setup() { + tm rtc_ctime; + max3133x_status_reg_t status_reg; + MAX3133X::alarm_period_t period; + bool is_enabled; + + pinMode(LED_BUILTIN, OUTPUT); + pinMode(INTAb, INPUT); + pinMode(INTBb, INPUT); + + Serial.begin(9600); + Serial.println("MAX3133x RTC Alarm Example"); + + Wire.setClock(400000); + + rtc = new MAX31331(&Wire); + + if(rtc->begin()) { + Serial.println("Error while rtc begin!"); + return; + } + + // Disable Clock in/out to configure pins as interrupt. + if (rtc->clkout_disable()) { + Serial.println("Error while disable CLKOUT!"); + return; + } else { + Serial.println("CLKOUT Disable"); + } + + set_test_time(&rtc_ctime); + if (rtc->set_time(&rtc_ctime)) { + Serial.println("Error while set time!"); + return; + } else { + Serial.println("Set Time"); + } + + if (rtc->get_time(&rtc_ctime)) { + Serial.println("Error while getting time!"); + return; + } else { + print_time(&rtc_ctime); + } + + if (rtc->interrupt_enable(A1IE|A2IE)) { + Serial.println("Error while setting interrupt!"); + return; + } else { + Serial.println("Alarm1 and Alarm2 interrupt Enable"); + } + + rtc_ctime.tm_hour = 12; + rtc_ctime.tm_min = 23; + rtc_ctime.tm_sec = 10; + rtc_ctime.tm_mday = 10; + rtc_ctime.tm_wday = 4; + rtc_ctime.tm_mon = 8; + rtc_ctime.tm_year = 2022 - 1900; + + if (rtc->set_alarm(MAX3133X::ALARM1, &rtc_ctime, MAX3133X::ALARM_PERIOD_EVERYMINUTE)) { + Serial.println("Error while setting alarm1!"); + return; + } else { + Serial.println("Set Alarm1"); + } + + rtc_ctime.tm_hour = 23; + rtc_ctime.tm_min = 0; + rtc_ctime.tm_sec = 15; + rtc_ctime.tm_mday = 11; + rtc_ctime.tm_wday = 5; + rtc_ctime.tm_mon = 9; + rtc_ctime.tm_year = 2023 - 1900; + + if (rtc->set_alarm(MAX3133X::ALARM2, &rtc_ctime, MAX3133X::ALARM_PERIOD_HOURLY)) { + Serial.println("Error while setting alarm2!"); + return; + } else { + Serial.println("Set Alarm2"); + } + + if (rtc->get_alarm(MAX3133X::ALARM1, &rtc_ctime, &period, &is_enabled)) { + Serial.println("Error while get_alarm1!"); + return; + } else { + Serial.print("Alarm1 Periodicity : "); + print_period(period); + is_enabled ? Serial.println("Alarm1 Enabled") : Serial.println("Alarm1 Disabled"); + Serial.println("Alarm1 Time : "); + print_time(&rtc_ctime); + } + + if (rtc->get_alarm(MAX3133X::ALARM2, &rtc_ctime, &period, &is_enabled)) { + Serial.println("Error while get_alarm2!"); + return; + } else { + Serial.print("Alarm2 Periodicity : "); + print_period(period); + is_enabled ? Serial.println("Alarm2 Enabled") : Serial.println("Alarm2 Disabled"); + Serial.println("Alarm2 Time : "); + print_time(&rtc_ctime); + } + + if (rtc->get_status_reg(&status_reg)) { + Serial.println("Error while get status register!"); + return; + } else { + Serial.print("Register : 0x");Serial.println(status_reg.raw); + Serial.print("Alarm1 is ");Serial.println(status_reg.bits.a1f); + Serial.print("Alarm2 is ");Serial.println(status_reg.bits.a2f); + Serial.print("Timer interrupt flag is ");Serial.println(status_reg.bits.tif); + Serial.print("Digital (DIN) interrupt flag is ");Serial.println(status_reg.bits.dif); + Serial.print("VBAT Low flag is ");Serial.println(status_reg.bits.vbatlow); + Serial.print("Power Fail flag is ");Serial.println(status_reg.bits.pfail); + Serial.print("Oscillator stop flag is ");Serial.println(status_reg.bits.osf); + + (status_reg.bits.psdect == 1) ? Serial.println("Part is running on VBAT") : Serial.println("Part is running on VCC"); + } + + attachInterrupt(digitalPinToInterrupt(INTAb), rtc_interrupt_handler, FALLING); + attachInterrupt(digitalPinToInterrupt(INTBb), rtc_interrupt_handler, FALLING); +} + +void loop(){ + tm rtc_ctime; + uint16_t subsec; + max3133x_int_en_reg_t int_en_reg; + max3133x_status_reg_t status_reg; + + if (interrupt_occured) { + interrupt_occured = false; + Serial.println("Interrupt!"); + + Serial.print("INTAb: "); + Serial.print(digitalRead(INTAb)); + Serial.print(", INTBb: "); + Serial.println(digitalRead(INTBb)); + + if (rtc->get_status_reg(&status_reg)) + Serial.println("Read status register failed!"); + + if (rtc->get_interrupt_reg(&int_en_reg)) + Serial.println("Read interrupt enable register failed!"); + + if ((status_reg.bits.a1f) && (int_en_reg.bits.a1ie)) { + Serial.println("Alarm1 Interrupt"); + } else if ((status_reg.bits.a2f) && (int_en_reg.bits.a2ie)) { + Serial.println("Alarm2 Interrupt"); + } else if ((status_reg.bits.tif) && (int_en_reg.bits.tie)) { + Serial.println("Timer Interrupt"); + } else if ((status_reg.bits.dif) && (int_en_reg.bits.die)) { + Serial.println("DIN Interrupt"); + } else { + Serial.println("Other Interrupt!"); + Serial.print("status_reg: "); + Serial.println(status_reg.raw); + Serial.print("int_en_reg: "); + Serial.println(int_en_reg.raw); + } + + if (rtc->get_time(&rtc_ctime, &subsec)) + Serial.println("Error while getting time!"); + else + print_time(&rtc_ctime, &subsec); + } + + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); +} \ No newline at end of file diff --git a/examples/MAX3133X/Sleep/Sleep.ino b/examples/MAX3133X/Sleep/Sleep.ino new file mode 100644 index 0000000..fca992d --- /dev/null +++ b/examples/MAX3133X/Sleep/Sleep.ino @@ -0,0 +1,218 @@ +#include + +MAX31334 *rtc; + +int INTAb = PIN2; +int INTBb = PIN3; +volatile bool interrupt_occured = false; + +const char *days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + +void set_test_time(tm * testTime) +{ + /*Date: 31/12/2022 Day: Saturday + Time: 23:59:45*/ + testTime->tm_sec = 45; + testTime->tm_min = 59; + testTime->tm_hour = 23; + testTime->tm_mday = 31; + testTime->tm_mon = 11; + testTime->tm_year = 122; + testTime->tm_wday = 5; + testTime->tm_yday = 0; + testTime->tm_isdst = 0; +} + +void print_time(tm *rtc_ctime, uint16_t *sub_sec = NULL) { + Serial.print("Date: "); + Serial.print(rtc_ctime->tm_mday); + Serial.print("/"); + Serial.print(rtc_ctime->tm_mon + 1); + Serial.print("/"); + Serial.print(rtc_ctime->tm_year + 1900); + Serial.print(" Day: "); + Serial.println(days[rtc_ctime->tm_wday]); + + Serial.print("Time: "); + Serial.print(rtc_ctime->tm_hour); + Serial.print(":"); + Serial.print(rtc_ctime->tm_min); + Serial.print(":"); + if(sub_sec == NULL) { + Serial.println(rtc_ctime->tm_sec); + } else { + Serial.print(rtc_ctime->tm_sec); + Serial.print("."); + Serial.println(*sub_sec); + } +} + +void rtc_interrupt_handler() { + interrupt_occured = true; +} + +void setup() { + max3133x_status_reg_t status_reg; + tm rtc_ctime; + + pinMode(LED_BUILTIN, OUTPUT); + pinMode(INTAb, INPUT); + pinMode(INTBb, INPUT); + + Serial.begin(9600); + Serial.println("MAX3133x RTC Sleep Example"); + + Wire.setClock(400000); + + rtc = new MAX31334(&Wire); + + if(rtc->begin()) { + Serial.println("Error while rtc begin!"); + return; + } + + set_test_time(&rtc_ctime); + if (rtc->set_time(&rtc_ctime)) { + Serial.println("Error while set time!"); + return; + } else { + Serial.println("Set Time"); + } + + // Disable Clock in/out to configure pins as interrupt. + if (rtc->clkout_disable()) { + Serial.println("Error while disable CLKOUT!"); + return; + } else { + Serial.println("CLKOUT Disable"); + } + + if (rtc->get_time(&rtc_ctime)) { + Serial.println("Error while getting time!"); + return; + } else { + print_time(&rtc_ctime); + } + + if (rtc->interrupt_enable(TIE|DIE)) { + Serial.println("Error while setting interrupt!"); + return; + } else { + Serial.println("Interrupt Enable"); + } + + if (rtc->timer_init(85, true, MAX3133X::TIMER_FREQ_16HZ)) { + Serial.println("Error while Timer Init!"); + return; + } else { + Serial.println("Timer Init"); + } + + if (rtc->wakeup_enable(TWE|DIE)) { + Serial.println("Error while wakeup enable!"); + return; + } else { + Serial.println("Wakeup Enable"); + } + + if (rtc->sleep_enter()) { + Serial.println("Error while Sleep Enter"); + return; + } else { + Serial.println("Sleep Enter"); + } + + delay(1000); + + if (rtc->sleep_exit()) { + Serial.println("Error while Sleep Exit"); + return; + } else { + Serial.println("Sleep Exit"); + } + + if (rtc->get_status_reg(&status_reg)) { + Serial.println("Error while get status register!"); + return; + } else { + Serial.print("Register : 0x");Serial.println(status_reg.raw); + Serial.print("Alarm1 is ");Serial.println(status_reg.bits.a1f); + Serial.print("Alarm2 is ");Serial.println(status_reg.bits.a2f); + Serial.print("Timer interrupt flag is ");Serial.println(status_reg.bits.tif); + Serial.print("Digital (DIN) interrupt flag is ");Serial.println(status_reg.bits.dif); + Serial.print("VBAT Low flag is ");Serial.println(status_reg.bits.vbatlow); + Serial.print("Power Fail flag is ");Serial.println(status_reg.bits.pfail); + Serial.print("Oscillator stop flag is ");Serial.println(status_reg.bits.osf); + + (status_reg.bits.psdect == 1) ? Serial.println("Part is running on VBAT") : Serial.println("Part is running on VCC"); + } + + if (rtc->timer_start()) { + Serial.println("Error while Timer Start!"); + return; + } else { + Serial.println("Timer Start"); + } + + if (rtc->sleep_enter()) { + Serial.println("Error while Sleep Enter"); + return; + } else { + Serial.println("Sleep Enter"); + } + + attachInterrupt(digitalPinToInterrupt(INTAb), rtc_interrupt_handler, FALLING); + attachInterrupt(digitalPinToInterrupt(INTBb), rtc_interrupt_handler, FALLING); +} + +void loop(){ + tm rtc_ctime; + uint16_t subsec; + max3133x_int_en_reg_t int_en_reg; + max3133x_status_reg_t status_reg; + + if (interrupt_occured) { + interrupt_occured = false; + Serial.println("Interrupt! "); + + Serial.print("INTAb: "); + Serial.print(digitalRead(INTAb)); + Serial.print(" ,INTBb: "); + Serial.println(digitalRead(INTBb)); + + if (rtc->get_status_reg(&status_reg)) + Serial.println("Read status register failed!"); + + if (rtc->get_interrupt_reg(&int_en_reg)) + Serial.println("Read interrupt enable register failed!"); + + if ((status_reg.bits.a1f) && (int_en_reg.bits.a1ie)) { + Serial.println("Alarm1 Interrupt! "); + } else if ((status_reg.bits.a2f) && (int_en_reg.bits.a2ie)) { + Serial.println("Alarm2 Interrupt! "); + } else if ((status_reg.bits.tif) && (int_en_reg.bits.tie)) { + Serial.println("Timer Interrupt! "); + } else if ((status_reg.bits.dif) && (int_en_reg.bits.die)) { + Serial.println("DIN Interrupt! "); + } else { + Serial.println("Other Interrupt! "); + Serial.print("status_reg.raw: ");Serial.println(status_reg.raw); + Serial.print("int_en_reg.raw: ");Serial.println(int_en_reg.raw); + } + + if (rtc->get_time(&rtc_ctime, &subsec)) + Serial.println("Error while getting time!"); + else + print_time(&rtc_ctime, &subsec); + } + + if (rtc->get_sleep_state()) + Serial.println("In sleep state"); + else + Serial.println("Not in sleep state"); + + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); +} \ No newline at end of file diff --git a/examples/MAX3133X/Timer/Timer.ino b/examples/MAX3133X/Timer/Timer.ino new file mode 100644 index 0000000..58fa59d --- /dev/null +++ b/examples/MAX3133X/Timer/Timer.ino @@ -0,0 +1,182 @@ +#include + +MAX31331 *rtc; + +int INTAb = PIN2; +int INTBb = PIN3; +volatile bool interrupt_occured = false; + +const char *days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + +void set_test_time(tm * testTime) +{ + /*Date: 31/12/2022 Day: Saturday + Time: 23:59:45*/ + testTime->tm_sec = 45; + testTime->tm_min = 59; + testTime->tm_hour = 23; + testTime->tm_mday = 31; + testTime->tm_mon = 11; + testTime->tm_year = 122; + testTime->tm_wday = 5; + testTime->tm_yday = 0; + testTime->tm_isdst = 0; +} + +void print_time(tm *rtc_ctime, uint16_t *sub_sec = NULL) { + Serial.print("Date: "); + Serial.print(rtc_ctime->tm_mday); + Serial.print("/"); + Serial.print(rtc_ctime->tm_mon + 1); + Serial.print("/"); + Serial.print(rtc_ctime->tm_year + 1900); + Serial.print(" Day: "); + Serial.println(days[rtc_ctime->tm_wday]); + + Serial.print("Time: "); + Serial.print(rtc_ctime->tm_hour); + Serial.print(":"); + Serial.print(rtc_ctime->tm_min); + Serial.print(":"); + if(sub_sec == NULL) { + Serial.println(rtc_ctime->tm_sec); + } else { + Serial.print(rtc_ctime->tm_sec); + Serial.print("."); + Serial.println(*sub_sec); + } +} + +void rtc_interrupt_handler() { + interrupt_occured = true; +} + +void setup() { + max3133x_status_reg_t status_reg; + tm rtc_ctime; + + pinMode(LED_BUILTIN, OUTPUT); + pinMode(INTAb, INPUT); + pinMode(INTBb, INPUT); + + Serial.begin(9600); + Serial.println("MAX3133x RTC Timer Example"); + + Wire.setClock(400000); + + rtc = new MAX31331(&Wire); + + if(rtc->begin()) { + Serial.println("Error while rtc begin!"); + return; + } + + set_test_time(&rtc_ctime); + if (rtc->set_time(&rtc_ctime)) { + Serial.println("Error while set time!"); + return; + } else { + Serial.println("Set Time"); + } + + if (rtc->get_time(&rtc_ctime)) { + Serial.println("Error while getting time!"); + return; + } else { + print_time(&rtc_ctime); + } + + if (rtc->interrupt_enable(TIE)) { + Serial.println("Error while setting interrupt!"); + return; + } else { + Serial.println("TIE interrupt Enable OK"); + } + + if (rtc->timer_init(250, true, MAX3133X::TIMER_FREQ_64HZ)) { + Serial.println("Error while timer_init!"); + return; + } else { + Serial.println("Timer Init OK"); + } + + if (rtc->get_status_reg(&status_reg)) { + Serial.println("Error while get status register!"); + return; + } else { + Serial.print("Register : 0x");Serial.println(status_reg.raw); + Serial.print("Alarm1 is ");Serial.println(status_reg.bits.a1f); + Serial.print("Alarm2 is ");Serial.println(status_reg.bits.a2f); + Serial.print("Timer interrupt flag is ");Serial.println(status_reg.bits.tif); + Serial.print("Digital (DIN) interrupt flag is ");Serial.println(status_reg.bits.dif); + Serial.print("VBAT Low flag is ");Serial.println(status_reg.bits.vbatlow); + Serial.print("Power Fail flag is ");Serial.println(status_reg.bits.pfail); + Serial.print("Oscillator stop flag is ");Serial.println(status_reg.bits.osf); + + (status_reg.bits.psdect == 1) ? Serial.println("Part is running on VBAT") : Serial.println("Part is running on VCC"); + } + + attachInterrupt(digitalPinToInterrupt(INTAb), rtc_interrupt_handler, FALLING); + attachInterrupt(digitalPinToInterrupt(INTBb), rtc_interrupt_handler, FALLING); + + if (rtc->timer_start()) { + Serial.println("Error while timer_start!"); + return; + } else { + Serial.println("Timer Start OK"); + } +} + +void loop(){ + tm rtc_ctime; + uint16_t subsec; + max3133x_int_en_reg_t int_en_reg; + max3133x_status_reg_t status_reg; + + if (interrupt_occured) { + interrupt_occured = false; + Serial.println("Interrupt! "); + + Serial.print("INTAb: "); + Serial.print(digitalRead(INTAb)); + Serial.print(" ,INTBb: "); + Serial.println(digitalRead(INTBb)); + + if (rtc->get_status_reg(&status_reg)) + Serial.println("Read status register failed!"); + + if (rtc->get_interrupt_reg(&int_en_reg)) + Serial.println("Read interrupt enable register failed!"); + + if ((status_reg.bits.a1f) && (int_en_reg.bits.a1ie)) { + Serial.println("Alarm1 Interrupt! "); + } else if ((status_reg.bits.a2f) && (int_en_reg.bits.a2ie)) { + Serial.println("Alarm2 Interrupt! "); + } else if ((status_reg.bits.tif) && (int_en_reg.bits.tie)) { + Serial.println("Timer Interrupt! "); + } else if ((status_reg.bits.dif) && (int_en_reg.bits.die)) { + Serial.println("DIN Interrupt! "); + } else { + Serial.println("Other Interrupt! "); + Serial.print("status_reg.raw: ");Serial.println(status_reg.raw); + Serial.print("int_en_reg.raw: ");Serial.println(int_en_reg.raw); + } + + if (rtc->get_time(&rtc_ctime, &subsec)) + Serial.println("Error while getting time!"); + else + print_time(&rtc_ctime, &subsec); + } + + Serial.print("Timer Val: "); + Serial.println(rtc->timer_get()); + Serial.print("INTAb: "); + Serial.print(digitalRead(INTAb)); + Serial.print(" ,INTBb: "); + Serial.println(digitalRead(INTBb)); + + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); +} \ No newline at end of file diff --git a/examples/MAX3133X/Timestamp/Timestamp.ino b/examples/MAX3133X/Timestamp/Timestamp.ino new file mode 100644 index 0000000..e33037c --- /dev/null +++ b/examples/MAX3133X/Timestamp/Timestamp.ino @@ -0,0 +1,174 @@ +#include + +MAX31331 *rtc; +uint8_t ts_print_flag = 0; + +const char *days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + +void set_test_time(tm * testTime) +{ + /*Date: 31/12/2022 Day: Saturday + Time: 23:59:45*/ + testTime->tm_sec = 45; + testTime->tm_min = 59; + testTime->tm_hour = 23; + testTime->tm_mday = 31; + testTime->tm_mon = 11; + testTime->tm_year = 122; + testTime->tm_wday = 5; + testTime->tm_yday = 0; + testTime->tm_isdst = 0; +} + +void print_time(tm *rtc_ctime, uint16_t *sub_sec = NULL, bool ts = false) { + Serial.print("Date: "); + Serial.print(rtc_ctime->tm_mday); + Serial.print("/"); + Serial.print(rtc_ctime->tm_mon + 1); + Serial.print("/"); + + if(!ts) { + Serial.print(rtc_ctime->tm_year + 1900); + Serial.print(" Day: "); + Serial.println(days[rtc_ctime->tm_wday]); + } else { + Serial.println(rtc_ctime->tm_year + 1900); + } + + Serial.print("Time: "); + Serial.print(rtc_ctime->tm_hour); + Serial.print(":"); + Serial.print(rtc_ctime->tm_min); + Serial.print(":"); + if(sub_sec == NULL) { + Serial.println(rtc_ctime->tm_sec); + } else { + Serial.print(rtc_ctime->tm_sec); + Serial.print("."); + Serial.println(*sub_sec); + } +} + +void setup() { + tm rtc_ctime; + uint16_t subsec; + max3133x_status_reg_t status_reg; + + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(9600); + Serial.println("MAX3133x RTC Timestamp Example"); + + Wire.setClock(400000); + + rtc = new MAX31331(&Wire); + + if(rtc->begin()) { + Serial.println("Error while rtc begin!"); + return; + } + + set_test_time(&rtc_ctime); + if (rtc->set_time(&rtc_ctime)) { + Serial.println("Error while set time!"); + return; + } else { + Serial.println("Set Time"); + } + + if (rtc->get_time(&rtc_ctime, &subsec)) { + Serial.println("Error while getting time!"); + return; + } else { + print_time(&rtc_ctime, &subsec); + } + + if (rtc->timestamp_registers_reset()) { + Serial.println("Error while timestamp_registers_reset!"); + return; + } else { + Serial.println("Timestamp Registers Reset"); + } + + if (rtc->timestamp_record_enable(TSVLOW|TSPWM|TSDIN)) { + Serial.println("Error while setting timestamps!"); + return; + } else { + Serial.println("Timestamp Record Enable for TSVLOW, TSPWM, TSDIN"); + } + + if (rtc->timestamp_function_enable()) { + Serial.println("Error while timestamp_function_enable!"); + return; + } else { + Serial.println("Timestamp Enable"); + } + + if (rtc->trickle_charger_enable(MAX3133X::TRICKLE_CHARGER_3K, false)) { + Serial.println("Error while trickle_charger_enable!"); + return; + } else { + Serial.println("Trickle Charger Enable"); + } + + if (rtc->get_status_reg(&status_reg)) { + Serial.println("Error while get status register!"); + return; + } else { + Serial.print("Register : 0x");Serial.println(status_reg.raw); + Serial.print("Alarm1 is ");Serial.println(status_reg.bits.a1f); + Serial.print("Alarm2 is ");Serial.println(status_reg.bits.a2f); + Serial.print("Timer interrupt flag is ");Serial.println(status_reg.bits.tif); + Serial.print("Digital (DIN) interrupt flag is ");Serial.println(status_reg.bits.dif); + Serial.print("VBAT Low flag is ");Serial.println(status_reg.bits.vbatlow); + Serial.print("Power Fail flag is ");Serial.println(status_reg.bits.pfail); + Serial.print("Oscillator stop flag is ");Serial.println(status_reg.bits.osf); + + (status_reg.bits.psdect == 1) ? Serial.println("Part is running on VBAT") : Serial.println("Part is running on VCC"); + } +} + +void loop(){ + tm rtc_ctime; + MAX3133X::timestamp_t timestamp; + int ret; + + for (int ts_idx = 0; ts_idx < MAX3133X::NUM_OF_TS; ts_idx++) { + ret = rtc->get_timestamp(ts_idx, ×tamp); + if (!ret) { + if (timestamp.ts_trigger) { + if ((ts_print_flag & (1< VCC switch"); + } else if (timestamp.ts_trigger == MAX3133X::VBATF) { + Serial.println("Triggered by VCC -> VBAT switch"); + } else if (timestamp.ts_trigger == MAX3133X::VLOWF) { + Serial.println("Triggered by VLOW detection"); + } else { + Serial.println("Undefined Trigger"); + break; + } + + print_time(×tamp.ctime, ×tamp.sub_sec, true); + ts_print_flag |= (1< + +MAX31341 rtc(&Wire, MAX31341_I2C_ADDRESS); +MAX31341::reg_status_t g_stat; + +// Pin Number that connects to MAX31341 interrupt pin +// Please update pin_interrupt depend on your target board connection +int pin_inta = 3; + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("ALARM use case example:"); + Serial.println("The sensor will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous example configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_inta, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.configure_inta_clkin_pin(MAX31341::CONFIGURE_PIN_AS_INTA); + if (ret) { + Serial.println("Configure inta failed!"); + } + + ret = rtc.set_alarm(MAX31341::ALARM1, &rtc_ctime, MAX31341::ALARM_PERIOD_EVERYSECOND); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31341::INTR_ID_ALARM1); + if (ret) { + Serial.println("IRQ enalbe failed!"); + } + + rtc.irq_clear_flag(); +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_inta); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.a1f) { // is alarm1 flag set? + print_time(); + } + } +} diff --git a/examples/MAX31341/Alarm2/Alarm2.ino b/examples/MAX31341/Alarm2/Alarm2.ino new file mode 100644 index 0000000..e9cde2c --- /dev/null +++ b/examples/MAX31341/Alarm2/Alarm2.ino @@ -0,0 +1,96 @@ +#include + +MAX31341 rtc(&Wire, MAX31341_I2C_ADDRESS); +MAX31341::reg_status_t g_stat; + +// Pin Number that connects to MAX31341 interrupt pin +// Please update pin_interrupt depend on your target board connection +int pin_inta = 3; + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("ALARM use case example:"); + Serial.println("The sensor will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous example configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_inta, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.configure_inta_clkin_pin(MAX31341::CONFIGURE_PIN_AS_INTA); + if (ret) { + Serial.println("Configure inta failed!"); + } + + ret = rtc.set_alarm(MAX31341::ALARM2, &rtc_ctime, MAX31341::ALARM_PERIOD_EVERYMINUTE); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31341::INTR_ID_ALARM2); + if (ret) { + Serial.println("IRQ enalbe failed!"); + } + + rtc.irq_clear_flag(); + print_time(); // print current time +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_inta); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.a2f) { // is alarm1 flag set? + print_time(); + } + } +} diff --git a/examples/MAX31341/Time/Time.ino b/examples/MAX31341/Time/Time.ino new file mode 100644 index 0000000..e826eec --- /dev/null +++ b/examples/MAX31341/Time/Time.ino @@ -0,0 +1,51 @@ +#include + +MAX31341 rtc(&Wire, MAX31341_I2C_ADDRESS); + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("Set get rtc use case example:"); + Serial.println("RTC will be set to specific value then it will be read every second"); + Serial.println(" "); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } +} + +void loop() { + delay(1000); // wait a little + print_time(); +} diff --git a/examples/MAX31341/Timer/Timer.ino b/examples/MAX31341/Timer/Timer.ino new file mode 100644 index 0000000..6c9d78e --- /dev/null +++ b/examples/MAX31341/Timer/Timer.ino @@ -0,0 +1,73 @@ +#include + +MAX31341 rtc(&Wire, MAX31341_I2C_ADDRESS); +MAX31341::reg_status_t g_stat; + +int pin_inta = 3;// interrupt pins that connects to MAX31341 +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("RTC timer use case example:"); + Serial.println(" "); + + pinMode(pin_inta, INPUT_PULLUP); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + int ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + rtc.timer_stop(); + rtc.timer_init(16, true, MAX31341::TIMER_FREQ_16HZ); + rtc.irq_enable(MAX31341::INTR_ID_TIMER); + rtc.timer_start(); +} + +void loop() { + + int pin_state = digitalRead(pin_inta); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.tif) { // is timer flag set? + print_time(); + } + } +} diff --git a/examples/MAX31341/Timer_Counter/Timer_Counter.ino b/examples/MAX31341/Timer_Counter/Timer_Counter.ino new file mode 100644 index 0000000..d121f57 --- /dev/null +++ b/examples/MAX31341/Timer_Counter/Timer_Counter.ino @@ -0,0 +1,41 @@ +#include + +MAX31341 rtc(&Wire, MAX31341_I2C_ADDRESS); + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("MAX31341 timer use case example:"); + Serial.println("MAX31341 timer module will be configured then timer count value will be display"); + Serial.println(" "); + + rtc.begin(); + + MAX31341::timer_freq_t freq = MAX31341::TIMER_FREQ_16HZ; + uint8_t value = 0xFF; + + ret = rtc.timer_init(value, true, freq); + if (ret) { + Serial.println("Timer init failed!"); + } + + ret = rtc.timer_start(); + if (ret) { + Serial.println("Timer start failed!"); + } +} + +void loop() { + static uint8_t last_count = 0; + uint8_t current_count = 0; + + rtc.timer_get(current_count); + if (current_count != last_count) { + last_count = current_count; + Serial.print("TMR Counter: "); + Serial.println(current_count, HEX); + } + +} diff --git a/examples/MAX31343/Alarm1/Alarm1.ino b/examples/MAX31343/Alarm1/Alarm1.ino new file mode 100644 index 0000000..8075eba --- /dev/null +++ b/examples/MAX31343/Alarm1/Alarm1.ino @@ -0,0 +1,91 @@ +#include + +MAX31343 rtc(&Wire, MAX31343_I2C_ADDRESS); +MAX31343::reg_status_t g_stat; + +// Pin Number that connects to MAX31343 interrupt pin +// Please update pin_interrupt depend on your target board connection +int pin_interrupt = 2; + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("ALARM use case example:"); + Serial.println("The sensor will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous example configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_interrupt, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.set_alarm(MAX31343::ALARM1, &rtc_ctime, MAX31343::ALARM_PERIOD_EVERYSECOND); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31343::INTR_ID_ALARM1); + if (ret) { + Serial.println("IRQ enalbe failed!"); + } + + rtc.irq_clear_flag(); + print_time(); // print current time +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_interrupt); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.a1f) { // is alarm1 flag set? + print_time(); + } + } +} diff --git a/examples/MAX31343/Alarm2/Alarm2.ino b/examples/MAX31343/Alarm2/Alarm2.ino new file mode 100644 index 0000000..bbea720 --- /dev/null +++ b/examples/MAX31343/Alarm2/Alarm2.ino @@ -0,0 +1,91 @@ +#include + +MAX31343 rtc(&Wire, MAX31343_I2C_ADDRESS); +MAX31343::reg_status_t g_stat; + +// Pin Number that connects to MAX31343 interrupt pin +// Please update pin_interrupt depend on your target board connection +int pin_interrupt = 2; + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("ALARM use case example:"); + Serial.println("The sensor will be configured to generate periodic alarm."); + Serial.println("Note: You may need to unplug and plug target board while switching between examples, "); + Serial.println("to previous example configuration to be deleted."); + Serial.println(" "); + + // Set alarm pin as input + pinMode(pin_interrupt, INPUT); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + ret = rtc.set_alarm(MAX31343::ALARM2, &rtc_ctime, MAX31343::ALARM_PERIOD_EVERYMINUTE); + if (ret) { + Serial.println("Set alarm failed!"); + } + + ret = rtc.irq_enable(MAX31343::INTR_ID_ALARM2); + if (ret) { + Serial.println("IRQ enalbe failed!"); + } + + rtc.irq_clear_flag(); + print_time(); // print current time +} + +void loop() { + + //Serial.println("Tick"); + + int pin_state = digitalRead(pin_interrupt); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.a2f) { // is alarm2 flag set? + print_time(); + } + } +} diff --git a/examples/MAX31343/Temperature/Temperature.ino b/examples/MAX31343/Temperature/Temperature.ino new file mode 100644 index 0000000..1d22e29 --- /dev/null +++ b/examples/MAX31343/Temperature/Temperature.ino @@ -0,0 +1,56 @@ +#include + +MAX31343 rtc(&Wire, MAX31343_I2C_ADDRESS); + +#define MAX_LOOP_COUNTER 250 + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("RTC temperature use case example:"); + Serial.println("Temperature will be read periodically"); + Serial.println(" "); + + rtc.begin(); +} + +void loop() { + int ret; + + delay(500); // wait a little + + ret = rtc.start_temp_conversion(); + if (ret) { + Serial.println("Start temperature converstion failed!"); + return; + } + + int counter = MAX_LOOP_COUNTER; // max wait time + do { + delay(1); + if (rtc.is_temp_ready() == 0) { // returns 0 on success + break; // means converstion done, so break + } + } while(--counter); + + if (counter == 0) { + Serial.println("Timeout occur!"); + } else { + float temp = 0; + ret = rtc.get_temp(temp); + if (ret) { + Serial.println("Temperature read failed!"); + } else { + Serial.print("Conversion time: "); + Serial.print( MAX_LOOP_COUNTER - counter ); + Serial.print(" ms"); + + Serial.print(" "); + Serial.print("Temperature: "); + Serial.print(temp, 2); + Serial.println(" Celsius"); + } + } +} diff --git a/examples/MAX31343/Time/Time.ino b/examples/MAX31343/Time/Time.ino new file mode 100644 index 0000000..068072d --- /dev/null +++ b/examples/MAX31343/Time/Time.ino @@ -0,0 +1,51 @@ +#include + +MAX31343 rtc(&Wire, MAX31343_I2C_ADDRESS); + +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + int ret; + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("RTC use case example:"); + Serial.println("RTC will be set to specific value then it will be read every second"); + Serial.println(" "); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } +} + +void loop() { + delay(1000); // wait a little + print_time(); +} diff --git a/examples/MAX31343/Timer/Timer.ino b/examples/MAX31343/Timer/Timer.ino new file mode 100644 index 0000000..45e8f2b --- /dev/null +++ b/examples/MAX31343/Timer/Timer.ino @@ -0,0 +1,73 @@ +#include + +MAX31343 rtc = MAX31343(&Wire, MAX31343_I2C_ADDRESS); +MAX31343::reg_status_t g_stat; + +int pin_interrupt = 2;// interrupt pins that connects to MAX31343 +struct tm rtc_ctime; + +void print_time(void) { + char buf[40]; + + int ret = rtc.get_time(&rtc_ctime); + if (ret) { + Serial.println("get_time failed!"); + return; + } + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &rtc_ctime); + Serial.println(buf); +} + +void setup() { + + Serial.begin(115200); + Serial.println("---------------------"); + Serial.println("RTC timer use case example:"); + Serial.println(" "); + + pinMode(pin_interrupt, INPUT_PULLUP); + + rtc.begin(); + + rtc_ctime.tm_year = 121; // years since 1900 + rtc_ctime.tm_mon = 10; // 0-11 + rtc_ctime.tm_mday = 24; // 1-31 + rtc_ctime.tm_hour = 15; // 0-23 + rtc_ctime.tm_min = 10; // 0-59 + rtc_ctime.tm_sec = 0; // 0-61 + // + rtc_ctime.tm_yday = 0; // 0-365 + rtc_ctime.tm_wday = 0; // 0-6 + rtc_ctime.tm_isdst = 0; // Daylight saving flag + + int ret = rtc.set_time(&rtc_ctime); + if (ret) { + Serial.println("Set time failed!"); + } + + rtc.timer_stop(); + rtc.timer_init(16, true, MAX31343::TIMER_FREQ_16HZ); + rtc.irq_enable(MAX31343::INTR_ID_TIMER); + rtc.timer_start(); +} + +void loop() { + + int pin_state = digitalRead(pin_interrupt); + + if (pin_state == LOW) { + int ret; + + // reading status byte will clear interrupt flags + ret = rtc.get_status(g_stat); + if (ret) { + Serial.println("Read status failed!"); + return; + } + + if (g_stat.bits.tif) { // is timer flag set? + print_time(); + } + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..28426fd --- /dev/null +++ b/keywords.txt @@ -0,0 +1,502 @@ + +AnalogRTCLib KEYWORD1 + +################################################ +# +# MAX3133X +# +################################################ +MAX31331 KEYWORD1 +MAX31334 KEYWORD1 +hour_format_t KEYWORD1 +alarm_period_t KEYWORD1 +alarm_no_t KEYWORD1 +data_ret_t KEYWORD1 +i2c_timeout_t KEYWORD1 +en_osc_t KEYWORD1 +dse_t KEYWORD1 +ddb_t KEYWORD1 +enclko_t KEYWORD1 +a1ac_t KEYWORD1 +dip_t KEYWORD1 +clko_hz_t KEYWORD1 +timer_freq_t KEYWORD1 +power_mgmt_supply_t KEYWORD1 +trickle_charger_ohm_t KEYWORD1 +ts_num_t KEYWORD1 +ts_trigger_t KEYWORD1 +timestamp_t KEYWORD1 +reg_addr_t KEYWORD1 +rtc_config_t KEYWORD1 +wsto_t KEYWORD1 + +rtc_config KEYWORD2 +get_rtc_config KEYWORD2 +read_register KEYWORD2 +write_register KEYWORD2 +begin KEYWORD2 +get_time KEYWORD2 +set_time KEYWORD2 +set_alarm KEYWORD2 +get_alarm KEYWORD2 +get_status_reg KEYWORD2 +get_interrupt_reg KEYWORD2 +interrupt_enable KEYWORD2 +interrupt_disable KEYWORD2 +sw_reset_assert KEYWORD2 +sw_reset_release KEYWORD2 +sw_reset KEYWORD2 +set_alarm1_auto_clear KEYWORD2 +set_din_polarity KEYWORD2 +data_retention_mode_enter KEYWORD2 +data_retention_mode_exit KEYWORD2 +i2c_timeout_enable KEYWORD2 +i2c_timeout_disable KEYWORD2 +oscillator_enable KEYWORD2 +oscillator_disable KEYWORD2 +clkout_enable KEYWORD2 +clkout_disable KEYWORD2 +set_clko_freq KEYWORD2 +get_clko_freq KEYWORD2 +timestamp_function_enable KEYWORD2 +timestamp_function_disable KEYWORD2 +timestamp_registers_reset KEYWORD2 +timestamp_overwrite_enable KEYWORD2 +timestamp_overwrite_disable KEYWORD2 +timestamp_record_enable KEYWORD2 +timestamp_record_disable KEYWORD2 +timer_start KEYWORD2 +timer_pause KEYWORD2 +timer_continue KEYWORD2 +timer_stop KEYWORD2 +timer_init KEYWORD2 +timer_get KEYWORD2 +battery_voltage_detector_enable KEYWORD2 +battery_voltage_detector_disable KEYWORD2 +supply_select KEYWORD2 +trickle_charger_enable KEYWORD2 +trickle_charger_disable KEYWORD2 +get_timestamp KEYWORD2 +offset_configuration KEYWORD2 +oscillator_flag_enable KEYWORD2 +oscillator_flag_disable KEYWORD2 +get_sleep_state KEYWORD2 +din_sleep_entry_enable KEYWORD2 +din_sleep_entry_disable KEYWORD2 +din_pin_debounce_enable KEYWORD2 +din_pin_debounce_disable KEYWORD2 +sleep_enter KEYWORD2 +sleep_exit KEYWORD2 +set_wait_state_timeout KEYWORD2 +get_wait_state_timeout KEYWORD2 +wakeup_enable KEYWORD2 +wakeup_disable KEYWORD2 + +MAX3133X_NO_ERR LITERAL1 +MAX3133X_NULL_VALUE_ERR LITERAL1 +MAX3133X_READ_REG_ERR LITERAL1 +MAX3133X_WRITE_REG_ERR LITERAL1 +MAX3133X_INVALID_TIME_ERR LITERAL1 +MAX3133X_INVALID_DATE_ERR LITERAL1 +MAX3133X_INVALID_MASK_ERR LITERAL1 +MAX3133X_INVALID_ALARM_PERIOD_ERR LITERAL1 +MAX3133X_ALARM_ONETIME_NOT_SUPP_ERR LITERAL1 +MAX3133X_ALARM_YEARLY_NOT_SUPP_ERR LITERAL1 +MAX3133X_ALARM_EVERYMINUTE_NOT_SUPP_ERR LITERAL1 +MAX3133X_ALARM_EVERYSECOND_NOT_SUPP_ERR LITERAL1 +MAX3133X_I2C_BUFF_ERR LITERAL1 +MAX3133X_I2C_END_TRANS_ERR LITERAL1 +ALARM_PERIOD_EVERYSECOND LITERAL1 +ALARM_PERIOD_EVERYMINUTE LITERAL1 +ALARM_PERIOD_HOURLY LITERAL1 +ALARM_PERIOD_DAILY LITERAL1 +ALARM_PERIOD_WEEKLY LITERAL1 +ALARM_PERIOD_MONTHLY LITERAL1 +ALARM_PERIOD_YEARLY LITERAL1 +ALARM_PERIOD_ONETIME LITERAL1 +ALARM1 LITERAL1 +ALARM2 LITERAL1 +HOUR24 LITERAL1 +HOUR12 LITERAL1 +A1IE LITERAL1 +A2IE LITERAL1 +TIE LITERAL1 +DIE LITERAL1 +VBATLOWIE LITERAL1 +PFAILE LITERAL1 +DOSF LITERAL1 +INT_ALL LITERAL1 +NUM_OF_INT LITERAL1 +NORMAL_OP_MODE LITERAL1 +DATA_RETENTION_MODE LITERAL1 +DISABLE_I2C_TIMEOUT LITERAL1 +ENABLE_I2C_TIMEOUT LITERAL1 +DISABLE_OSCILLATOR LITERAL1 +ENABLE_OSCILLATOR LITERAL1 +DIN_SLEEP_ENTRY_DISABLE LITERAL1 +DIN_SLEEP_ENTRY_ENABLE LITERAL1 +DIN_DEBOUNCE_DISABLE LITERAL1 +DIN_DEBOUNCE_ENABLE LITERAL1 +INTERRUPT LITERAL1 +CLOCK_OUTPUT LITERAL1 +BY_READING LITERAL1 +AFTER_10MS LITERAL1 +AFTER_500MS LITERAL1 +AFTER_5s LITERAL1 +FALLING_EDGE LITERAL1 +RISING_EDGE LITERAL1 +CLKOUT_1HZ LITERAL1 +CLKOUT_64HZ LITERAL1 +CLKOUT_1024KHZ LITERAL1 +CLKOUT_32KHZ_UNCOMP LITERAL1 +TSVLOW LITERAL1 +TSPWM LITERAL1 +TSDIN LITERAL1 +TIMER_FREQ_1024HZ LITERAL1 +TIMER_FREQ_256HZ LITERAL1 +TIMER_FREQ_64HZ LITERAL1 +TIMER_FREQ_16HZ LITERAL1 +POW_MGMT_SUPPLY_SEL_AUTO LITERAL1 +POW_MGMT_SUPPLY_SEL_VCC LITERAL1 +POW_MGMT_SUPPLY_SEL_VBAT LITERAL1 +TRICKLE_CHARGER_3K LITERAL1 +TRICKLE_CHARGER_3K_2 LITERAL1 +TRICKLE_CHARGER_6K LITERAL1 +TRICKLE_CHARGER_11K LITERAL1 +TS0 LITERAL1 +TS1 LITERAL1 +TS2 LITERAL1 +TS3 LITERAL1 +NUM_OF_TS LITERAL1 +NOT_TRIGGERED LITERAL1 +DINF LITERAL1 +VCCF LITERAL1 +VBATF LITERAL1 +VLOWF LITERAL1 +NUM_OF_TRIG LITERAL1 +WSTO_0MS LITERAL1 +WSTO_8MS LITERAL1 +WSTO_16MS LITERAL1 +WSTO_24MS LITERAL1 +WSTO_32MS LITERAL1 +WSTO_40MS LITERAL1 +WSTO_48MS LITERAL1 +WSTO_56MS LITERAL1 +A1WE LITERAL1 +A2WE LITERAL1 +TWE LITERAL1 +DWE LITERAL1 + + +################################################ +# +# MAX31341 +# +################################################ +MAX31341 KEYWORD1 +power_mgmt_mode_t KEYWORD1 +comp_thresh_t KEYWORD1 +power_mgmt_supply_t KEYWORD1 +trickle_charger_path_t KEYWORD1 +timer_freq_t KEYWORD1 +clkin_freq_t KEYWORD1 +sqw_out_freq_t KEYWORD1 +intr_id_t KEYWORD1 +alarm_no_t KEYWORD1 +alarm_period_t KEYWORD1 +config_inta_clkin_pin_t KEYWORD1 +config_intb_clkout_pin_t KEYWORD1 +sync_delay_t KEYWORD1 +reg_status_t KEYWORD1 +reg_cfg_t KEYWORD1 + + +begin KEYWORD2 +get_version KEYWORD2 +get_status KEYWORD2 +get_configuration KEYWORD2 +set_configuration KEYWORD2 +get_time KEYWORD2 +set_time KEYWORD2 +get_alarm KEYWORD2 +set_alarm KEYWORD2 +set_power_mgmt_mode KEYWORD2 +comparator_threshold_level KEYWORD2 +supply_select KEYWORD2 +supply_select KEYWORD2 +trickle_charger_enable KEYWORD2 +trickle_charger_disable KEYWORD2 +set_square_wave_frequency KEYWORD2 +set_clkin_frequency KEYWORD2 +configure_intb_clkout_pin KEYWORD2 +configure_inta_clkin_pin KEYWORD2 +timer_init KEYWORD2 +timer_get KEYWORD2 +timer_start KEYWORD2 +timer_pause KEYWORD2 +timer_continue KEYWORD2 +timer_stop KEYWORD2 +set_data_retention_mode KEYWORD2 +irq_enable KEYWORD2 +irq_disable KEYWORD2 +irq_clear_flag KEYWORD2 +sw_reset_assert KEYWORD2 +sw_reset_release KEYWORD2 +rtc_start KEYWORD2 +rtc_stop KEYWORD2 +nvram_size KEYWORD2 +nvram_write KEYWORD2 +nvram_read KEYWORD2 +read_register KEYWORD2 +write_register KEYWORD2 + + +POW_MGMT_MODE_COMPARATOR LITERAL1 +POW_MGMT_MODE_POWER_MANAGEMENT LITERAL1 +COMP_THRESH_1V3 LITERAL1 +COMP_THRESH_1V7 LITERAL1 +COMP_THRESH_2V0 LITERAL1 +COMP_THRESH_2V2 LITERAL1 + +POW_MGMT_SUPPLY_SEL_AUTO LITERAL1 +POW_MGMT_SUPPLY_SEL_VCC LITERAL1 +POW_MGMT_SUPPLY_SEL_AIN LITERAL1 + +TRICKLE_CHARGER_NO_CONNECTION LITERAL1 +TRICKLE_CHARGER_3K_S LITERAL1 +TRICKLE_CHARGER_6K_S LITERAL1 +TRICKLE_CHARGER_11K_S LITERAL1 +TRICKLE_CHARGER_3K_S_2 LITERAL1 +TRICKLE_CHARGER_6K_S_2 LITERAL1 +TRICKLE_CHARGER_11K_S_2 LITERAL1 + +TIMER_FREQ_1024HZ LITERAL1 +TIMER_FREQ_256HZ LITERAL1 +TIMER_FREQ_64HZ LITERAL1 +TIMER_FREQ_16HZ LITERAL1 + +CLKIN_FREQ_1HZ LITERAL1 +CLKIN_FREQ_50HZ LITERAL1 +CLKIN_FREQ_60HZ LITERAL1 +CLKIN_FREQ_32768HZ LITERAL1 + +SQW_OUT_FREQ_1HZ LITERAL1 +SQW_OUT_FREQ_4KHZ LITERAL1 +SQW_OUT_FREQ_8KHZ LITERAL1 +SQW_OUT_FREQ_32HZ LITERAL1 + +INTR_ID_ALARM1 LITERAL1 +INTR_ID_ALARM2 LITERAL1 +INTR_ID_TIMER LITERAL1 +INTR_ID_RESERVED LITERAL1 +INTR_ID_EXTERNAL LITERAL1 +INTR_ID_ANALOG LITERAL1 +INTR_ID_OSF LITERAL1 +INTR_ID_ALL LITERAL1 + +ALARM1 LITERAL1 +ALARM2 LITERAL1 + +ALARM_PERIOD_EVERYSECOND LITERAL1 +ALARM_PERIOD_EVERYMINUTE LITERAL1 +ALARM_PERIOD_HOURLY LITERAL1 +ALARM_PERIOD_DAILY LITERAL1 +ALARM_PERIOD_WEEKLY LITERAL1 +ALARM_PERIOD_MONTHLY LITERAL1 + +CONFIGURE_PIN_AS_INTA LITERAL1 +CONFIGURE_PIN_AS_CLKIN LITERAL1 + +CONFIGURE_PIN_AS_CLKOUT LITERAL1 +CONFIGURE_PIN_AS_INTB LITERAL1 + +SYNC_DLY_LESS_THAN_1SEC LITERAL1 +SYNC_DLY_LESS_THAN_100MS LITERAL1 +SYNC_DLY_LESS_THAN_10MS LITERAL1 + + +################################################ +# +# MAX31343 +# +################################################ +MAX31343 KEYWORD1 +clko_freq_t KEYWORD1 +comp_thresh_t KEYWORD1 +power_mgmt_supply_t KEYWORD1 +trickle_charger_ohm_t KEYWORD1 +timer_freq_t KEYWORD1 +intr_id_t KEYWORD1 +alarm_no_t KEYWORD1 +alarm_period_t KEYWORD1 +config_intb_clkout_pin_t KEYWORD1 +sqw_out_freq_t KEYWORD1 +ttsint_t KEYWORD1 +reg_status_t KEYWORD1 +reg_cfg_t KEYWORD1 + + +begin KEYWORD2 +get_status KEYWORD2 +get_configuration KEYWORD2 +set_configuration KEYWORD2 +get_time KEYWORD2 +set_time KEYWORD2 +get_alarm KEYWORD2 +set_alarm KEYWORD2 +powerfail_threshold_level KEYWORD2 +supply_select KEYWORD2 +trickle_charger_enable KEYWORD2 +trickle_charger_disable KEYWORD2 +set_square_wave_frequency KEYWORD2 +clko_enable KEYWORD2 +clko_disable KEYWORD2 +timer_init KEYWORD2 +timer_get KEYWORD2 +timer_start KEYWORD2 +timer_pause KEYWORD2 +timer_continue KEYWORD2 +timer_stop KEYWORD2 +set_data_retention_mode KEYWORD2 +start_temp_conversion KEYWORD2 +is_temp_ready KEYWORD2 +get_temp KEYWORD2 +irq_enable KEYWORD2 +irq_disable KEYWORD2 +irq_clear_flag KEYWORD2 +sw_reset_assert KEYWORD2 +sw_reset_release KEYWORD2 +rtc_start KEYWORD2 +rtc_stop KEYWORD2 +nvram_size KEYWORD2 +nvram_write KEYWORD2 +nvram_read KEYWORD2 +read_register KEYWORD2 +write_register KEYWORD2 + + +CLKO_FREQ_1HZ LITERAL1 +CLKO_FREQ_2HZ LITERAL1 +CLKO_FREQ_4HZ LITERAL1 +CLKO_FREQ_8HZ LITERAL1 +CLKO_FREQ_16HZ LITERAL1 +CLKO_FREQ_32HZ LITERAL1 +CLKO_FREQ_64HZ LITERAL1 +CLKO_FREQ_128HZ LITERAL1 +CLKO_FREQ_32000HZ LITERAL1 + +SQW_OUT_FREQ_1HZ LITERAL1 +SQW_OUT_FREQ_2HZ LITERAL1 +SQW_OUT_FREQ_4HZ LITERAL1 +SQW_OUT_FREQ_8HZ LITERAL1 +SQW_OUT_FREQ_16HZ LITERAL1 +SQW_OUT_FREQ_32HZ LITERAL1 + +COMP_THRESH_1V85 LITERAL1 +COMP_THRESH_2V15 LITERAL1 +COMP_THRESH_2V40 LITERAL1 + +POW_MGMT_SUPPLY_SEL_AUTO LITERAL1 +POW_MGMT_SUPPLY_SEL_VCC LITERAL1 +POW_MGMT_SUPPLY_SEL_VBACK LITERAL1 + +TRICKLE_CHARGER_3K_S LITERAL1 +TRICKLE_CHARGER_3K_2_S LITERAL1 +TRICKLE_CHARGER_6K_S LITERAL1 +TRICKLE_CHARGER_11K_S LITERAL1 +TRICKLE_CHARGER_3K_D_S LITERAL1 +TRICKLE_CHARGER_3K_2_D_S LITERAL1 +TRICKLE_CHARGER_6K_D_S LITERAL1 +TRICKLE_CHARGER_11K_D_S LITERAL1 +TRICKLE_CHARGER_NO_CONNECTION LITERAL1 + +TIMER_FREQ_1024HZ LITERAL1 +TIMER_FREQ_256HZ LITERAL1 +TIMER_FREQ_64HZ LITERAL1 +TIMER_FREQ_16HZ LITERAL1 + +INTR_ID_ALARM1 LITERAL1 +INTR_ID_ALARM2 LITERAL1 +INTR_ID_TIMER LITERAL1 +INTR_ID_TEMP LITERAL1 +INTR_ID_PFAIL LITERAL1 +INTR_ID_DOSF LITERAL1 +INTR_ID_ALL LITERAL1 + +ALARM1 LITERAL1 +ALARM2 LITERAL1 + +ALARM_PERIOD_EVERYSECOND LITERAL1 +ALARM_PERIOD_EVERYMINUTE LITERAL1 +ALARM_PERIOD_HOURLY LITERAL1 +ALARM_PERIOD_DAILY LITERAL1 +ALARM_PERIOD_WEEKLY LITERAL1 +ALARM_PERIOD_MONTHLY LITERAL1 +ALARM_PERIOD_YEARLY LITERAL1 +ALARM_PERIOD_ONETIME LITERAL1 + +CONFIGURE_PIN_AS_CLKOUT LITERAL1 +CONFIGURE_PIN_AS_INTB LITERAL1 + +TTS_INTERNAL_1SEC LITERAL1 +TTS_INTERNAL_2SEC LITERAL1 +TTS_INTERNAL_4SEC LITERAL1 +TTS_INTERNAL_8SEC LITERAL1 +TTS_INTERNAL_16SEC LITERAL1 +TTS_INTERNAL_32SEC LITERAL1 +TTS_INTERNAL_64SEC LITERAL1 +TTS_INTERNAL_128SEC LITERAL1 + + +################################################ +# +# MAX31328 +# +################################################ +MAX31328 KEYWORD1 +intr_id_t KEYWORD1 +alarm_no_t KEYWORD1 +alarm_period_t KEYWORD1 +sqw_out_freq_t KEYWORD1 +reg_status_t KEYWORD1 +reg_cfg_t KEYWORD1 + + +begin KEYWORD2 +get_status KEYWORD2 +get_configuration KEYWORD2 +set_configuration KEYWORD2 +get_time KEYWORD2 +set_time KEYWORD2 +get_alarm KEYWORD2 +set_alarm KEYWORD2 +irq_enable KEYWORD2 +irq_disable KEYWORD2 +irq_clear_flag KEYWORD2 +set_square_wave_frequency KEYWORD2 +start_temp_conversion KEYWORD2 +is_temp_ready KEYWORD2 +get_temp KEYWORD2 +read_register KEYWORD2 +write_register KEYWORD2 + + +INTR_ID_ALARM1 LITERAL1 +INTR_ID_ALARM2 LITERAL1 +INTR_ID_ALL LITERAL1 + +ALARM1 LITERAL1 +ALARM2 LITERAL1 + +ALARM_PERIOD_EVERYSECOND LITERAL1 +ALARM_PERIOD_EVERYMINUTE LITERAL1 +ALARM_PERIOD_HOURLY LITERAL1 +ALARM_PERIOD_DAILY LITERAL1 +ALARM_PERIOD_WEEKLY LITERAL1 +ALARM_PERIOD_MONTHLY LITERAL1 + +SQW_OUT_FREQ_1HZ LITERAL1 +SQW_OUT_FREQ_4KHZ LITERAL1 +SQW_OUT_FREQ_8KHZ LITERAL1 +SQW_OUT_FREQ_32KHZ LITERAL1 + diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..e966764 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=AnalogRTCLib +version=1.0.0 +author=Analog Devices +maintainer=Analog Devices +sentence=Library for Analog Devices Real Time Clocks. +paragraph=Analog Devices RTSs are low-current real time clocks with I2C, SPI or 1-Wire interfaces, integrated crystal and power management. +category=Timing +url=https://github.com/MaximIntegrated/MAX3133X-Arduino-Driver +architectures=* \ No newline at end of file diff --git a/src/AnalogRTCLibrary.h b/src/AnalogRTCLibrary.h new file mode 100644 index 0000000..435a78e --- /dev/null +++ b/src/AnalogRTCLibrary.h @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef _ANALOG_RTC_LIB_ +#define _ANALOG_RTC_LIB_ + + +#include "MAX31328/MAX31328.h" + +#include "MAX31341/MAX31341.h" + +#include "MAX31343/MAX31343.h" + +#include "MAX3133X/MAX3133X.h" + + +#endif /* _ANALOG_RTC_LIB_ */ diff --git a/src/MAX31328/MAX31328.cpp b/src/MAX31328/MAX31328.cpp new file mode 100644 index 0000000..83fbea4 --- /dev/null +++ b/src/MAX31328/MAX31328.cpp @@ -0,0 +1,613 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + + +#include +#include + + +#define GET_BIT_VAL(val, pos, mask) ( ( (val) & mask) >> pos ) +#define SET_BIT_VAL(val, pos, mask) ( ( ((int)val) << pos) & mask ) + +#define BCD2BIN(val) (((val) & 15) + ((val) >> 4) * 10) +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) + + +int MAX31328::read_register(uint8_t reg, uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + int counter = 0; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + + /* + stop = true, sends a stop message after transmission, releasing the I2C bus. + + stop = false, sends a restart message after transmission. + The bus will not be released, which prevents another master device from transmitting between messages. + This allows one master device to send multiple transmissions while in control. + */ + ret = m_i2c->endTransmission(false); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + if (ret != 0) { + m_i2c->begin(); // restart + return -1; + } + + // Read + m_i2c->requestFrom((char)m_slave_addr, (char)len, true); + + while (m_i2c->available()) { // slave may send less than requested + buf[counter++] = m_i2c->read(); // receive a byte as character + } + + // + if (counter != len) { + m_i2c->begin(); // restart + ret = -1; + } + + return ret; +} + +int MAX31328::write_register(uint8_t reg, const uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + m_i2c->write(buf, len); + ret = m_i2c->endTransmission(); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + + if (ret != 0) { + m_i2c->begin(); // restart + } + + return ret; +} + +/********************************************************************************/ +MAX31328::MAX31328(TwoWire *i2c, uint8_t i2c_addr) +{ + if (i2c == NULL) { + while (1); + } + + m_i2c = i2c; + m_slave_addr = i2c_addr; +} + +void MAX31328::begin(void) +{ + m_i2c->begin(); +} + +int MAX31328::get_status(reg_status_t &stat) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_STATUS, &val8); + if (ret) { + return ret; + } + + stat.bits.a1f = GET_BIT_VAL(val8, MAX31328_F_STATUS_A1F_POS, MAX31328_F_STATUS_A1F); + stat.bits.a2f = GET_BIT_VAL(val8, MAX31328_F_STATUS_A2F_POS, MAX31328_F_STATUS_A2F); + stat.bits.bsy = GET_BIT_VAL(val8, MAX31328_F_STATUS_BSY_POS, MAX31328_F_STATUS_BSY); + stat.bits.en32kHz = GET_BIT_VAL(val8, MAX31328_F_STATUS_EN32KHZ_POS, MAX31328_F_STATUS_EN32KHZ); + stat.bits.osf = GET_BIT_VAL(val8, MAX31328_F_STATUS_OSF_POS, MAX31328_F_STATUS_OSF); + + return ret; +} + +int MAX31328::set_status(reg_status_t stat) +{ + int ret; + uint8_t val8 = 0; + + val8 |= SET_BIT_VAL(stat.bits.a1f, MAX31328_F_STATUS_A1F_POS, MAX31328_F_STATUS_A1F); + val8 |= SET_BIT_VAL(stat.bits.a2f, MAX31328_F_STATUS_A2F_POS, MAX31328_F_STATUS_A2F); + val8 |= SET_BIT_VAL(stat.bits.bsy, MAX31328_F_STATUS_BSY_POS, MAX31328_F_STATUS_BSY); + val8 |= SET_BIT_VAL(stat.bits.en32kHz, MAX31328_F_STATUS_EN32KHZ_POS, MAX31328_F_STATUS_EN32KHZ); + val8 |= SET_BIT_VAL(stat.bits.osf, MAX31328_F_STATUS_OSF_POS, MAX31328_F_STATUS_OSF); + + ret = write_register(MAX31328_R_STATUS, &val8); + + return ret; +} + +int MAX31328::get_configuration(reg_cfg_t &cfg) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_CONTROL, &val8); + if (ret) { + return ret; + } + + cfg.bits.a1ie = GET_BIT_VAL(val8, MAX31328_F_CTRL_A1IE_POS, MAX31328_F_CTRL_A1IE); + cfg.bits.a2ie = GET_BIT_VAL(val8, MAX31328_F_CTRL_A2IE_POS, MAX31328_F_CTRL_A2IE); + cfg.bits.intcn = GET_BIT_VAL(val8, MAX31328_F_CTRL_INTCN_POS, MAX31328_F_CTRL_INTCN); + cfg.bits.rs = GET_BIT_VAL(val8, MAX31328_F_CTRL_RS_POS, MAX31328_F_CTRL_RS); + cfg.bits.conv = GET_BIT_VAL(val8, MAX31328_F_CTRL_CONV_POS, MAX31328_F_CTRL_CONV); + cfg.bits.bbsqw = GET_BIT_VAL(val8, MAX31328_F_CTRL_BBSQW_POS, MAX31328_F_CTRL_BBSQW); + cfg.bits.eosc = GET_BIT_VAL(val8, MAX31328_F_CTRL_EOSC_POS, MAX31328_F_CTRL_EOSC); + + return ret; +} + +int MAX31328::set_configuration(reg_cfg_t cfg) +{ + int ret; + uint8_t val8 = 0; + + val8 |= SET_BIT_VAL(cfg.bits.a1ie, MAX31328_F_CTRL_A1IE_POS, MAX31328_F_CTRL_A1IE); + val8 |= SET_BIT_VAL(cfg.bits.a2ie, MAX31328_F_CTRL_A2IE_POS, MAX31328_F_CTRL_A2IE); + val8 |= SET_BIT_VAL(cfg.bits.intcn, MAX31328_F_CTRL_INTCN_POS, MAX31328_F_CTRL_INTCN); + val8 |= SET_BIT_VAL(cfg.bits.rs, MAX31328_F_CTRL_RS_POS, MAX31328_F_CTRL_RS); + val8 |= SET_BIT_VAL(cfg.bits.conv, MAX31328_F_CTRL_CONV_POS, MAX31328_F_CTRL_CONV); + val8 |= SET_BIT_VAL(cfg.bits.bbsqw, MAX31328_F_CTRL_BBSQW_POS, MAX31328_F_CTRL_BBSQW); + val8 |= SET_BIT_VAL(cfg.bits.eosc, MAX31328_F_CTRL_EOSC_POS, MAX31328_F_CTRL_EOSC); + + ret = write_register(MAX31328_R_CONTROL, &val8); + + return ret; +} + +int MAX31328::set_time(const struct tm *time) +{ + int ret; + regs_rtc_time_t regs; + + if (time == NULL) { + return -1; + } + + /********************************************************* + * +----------+------+---------------------------+-------+ + * | Member | Type | Meaning | Range | + * +----------+------+---------------------------+-------+ + * | tm_sec | int | seconds after the minute | 0-61* | + * | tm_min | int | minutes after the hour | 0-59 | + * | tm_hour | int | hours since midnight | 0-23 | + * | tm_mday | int | day of the month | 1-31 | + * | tm_mon | int | months since January | 0-11 | + * | tm_year | int | years since 1900 | | + * | tm_wday | int | days since Sunday | 0-6 | + * | tm_yday | int | days since January 1 | 0-365 | + * | tm_isdst | int | Daylight Saving Time flag | | + * +----------+------+---------------------------+-------+ + * * tm_sec is generally 0-59. The extra range is to accommodate for leap + * seconds in certain systems. + *********************************************************/ + regs.seconds.bcd.value = BIN2BCD(time->tm_sec); + + regs.minutes.bcd.value = BIN2BCD(time->tm_min); + + regs.hours.bcd_format24.value= BIN2BCD(time->tm_hour); + + regs.day.bcd.value = BIN2BCD(time->tm_wday + 1); + + regs.date.bcd.value = BIN2BCD(time->tm_mday); + + regs.month.bcd.value = BIN2BCD(time->tm_mon + 1); + + if (time->tm_year >= 200) { + regs.month.bits.century = 1; + regs.year.bcd.value = BIN2BCD(time->tm_year - 200); + } else if (time->tm_year >= 100) { + regs.month.bits.century = 0; + regs.year.bcd.value = BIN2BCD(time->tm_year - 100); + } else { + //Invalid set date! + return -1; + } + + ret = write_register(MAX31328_R_SECONDS, (uint8_t *) ®s, sizeof(regs)); + + return ret; +} + +int MAX31328::set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period) +{ + int ret; + regs_alarm_t regs; + + if ((alarm_no == ALARM2) && (period == ALARM_PERIOD_EVERYSECOND)) { + return -1; /* Alarm2 does not support "once per second" alarm*/ + } + + /* Set Alarm Period */ + regs.sec.bits.axm1 = 1; + regs.min.bits.axm2 = 1; + regs.hrs.bits.axm3 = 1; + regs.day_date.bits.axm4 = 1; + regs.day_date.bits.dy_dt = 1; + + switch (period) { + case ALARM_PERIOD_EVERYSECOND: + // Do nothing + break; + case ALARM_PERIOD_EVERYMINUTE: + regs.sec.bits.axm1 = 0; + break; + case ALARM_PERIOD_HOURLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + break; + case ALARM_PERIOD_DAILY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + break; + case ALARM_PERIOD_WEEKLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + regs.day_date.bits.axm4 = 0; + break; + case ALARM_PERIOD_MONTHLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + regs.day_date.bits.axm4 = 0; + regs.day_date.bits.dy_dt = 0; + break; + default: + return -1; + } + + /* Convert time structure to alarm registers */ + regs.sec.bcd.value = BIN2BCD(alarm_time->tm_sec); + regs.min.bcd.value = BIN2BCD(alarm_time->tm_min); + regs.hrs.bcd_format24.value = BIN2BCD(alarm_time->tm_hour); + + if (regs.day_date.bits.dy_dt == 0) { + /* Date match */ + regs.day_date.bcd_date.value = BIN2BCD(alarm_time->tm_mday); + } else { + /* Day match */ + regs.day_date.bcd_day.value = BIN2BCD(alarm_time->tm_wday); + } + + + /* + * Write Registers + */ + uint8_t *ptr_regs = (uint8_t *)®s; + + if (alarm_no == ALARM1) { + ret = write_register(MAX31328_R_ALRM1_SECONDS, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + /* Discard sec starts from min register */ + ret = write_register(MAX31328_R_ALRM2_MINUTES, &ptr_regs[1], sizeof(regs_alarm_t)-1); + } + if (ret) { + return ret; + } + + return ret; +} + +int MAX31328::get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled) +{ + int ret; + regs_alarm_t regs; + uint8_t val8; + uint8_t *ptr_regs = (uint8_t *)®s; + + /* + * Read registers + */ + if (alarm_no == ALARM1) { + ret = read_register(MAX31328_R_ALRM1_SECONDS, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + regs.sec.raw = 0; /* zeroise second register for alarm2 */ + /* starts from min register (no sec register) */ + ret = read_register(MAX31328_R_ALRM2_MINUTES, &ptr_regs[1], sizeof(regs_alarm_t)-1); + } + + if (ret) { + return ret; + } + + /* + * Convert alarm registers to time structure + */ + alarm_time->tm_sec = BCD2BIN(regs.sec.bcd.value); + alarm_time->tm_min = BCD2BIN(regs.min.bcd.value); + alarm_time->tm_hour = BCD2BIN(regs.hrs.bcd_format24.value); + + if (regs.day_date.bits.dy_dt == 0) { /* date */ + alarm_time->tm_mday = BCD2BIN(regs.day_date.bcd_date.value); + } else { /* day */ + alarm_time->tm_wday = BCD2BIN(regs.day_date.bcd_day.value); + } + + /* + * Find period + */ + *period = (alarm_no == ALARM1) ? ALARM_PERIOD_EVERYSECOND : ALARM_PERIOD_EVERYMINUTE; + + if ((alarm_no == ALARM1) && (regs.sec.bits.axm1 == 0)) *period = ALARM_PERIOD_EVERYMINUTE; + if (regs.min.bits.axm2 == 0) *period = ALARM_PERIOD_HOURLY; + if (regs.hrs.bits.axm3 == 0) *period = ALARM_PERIOD_DAILY; + if (regs.day_date.bits.axm4 == 0) *period = ALARM_PERIOD_WEEKLY; + if (regs.day_date.bits.dy_dt == 0) *period = ALARM_PERIOD_MONTHLY; + + + /* + * Get enable status + */ + ret = read_register(MAX31328_R_CONTROL, &val8); + if (ret) { + return ret; + } + *is_enabled = (val8 & (uint8_t)alarm_no) ? true: false; + + return ret; +} + +int MAX31328::get_time(struct tm *time) +{ + int ret; + regs_rtc_time_t regs; + + if (time == NULL) { + return -1; + } + + ret = read_register(MAX31328_R_SECONDS, (uint8_t *) ®s, sizeof(regs)); + if (ret) { + return -1; + } + + /* tm_sec seconds [0,61] */ + time->tm_sec = BCD2BIN(regs.seconds.bcd.value); + + /* tm_min minutes [0,59] */ + time->tm_min = BCD2BIN(regs.minutes.bcd.value); + + /* tm_hour hour [0,23] */ + if (regs.hours.bits.f24_12) { + time->tm_hour = BCD2BIN(regs.hours.bcd_format12.value); + + if (regs.hours.bits.hr20_am_pm && (time->tm_hour != 12)) { + time->tm_hour += 12; + } + } else { + time->tm_hour = BCD2BIN(regs.hours.bcd_format24.value); + } + + /* tm_wday day of week [0,6] (Sunday = 0) */ + time->tm_wday = BCD2BIN(regs.day.bcd.value) - 1; + + /* tm_mday day of month [1,31] */ + time->tm_mday = BCD2BIN(regs.date.bcd.value); + + /* tm_mon month of year [0,11] */ + time->tm_mon = BCD2BIN(regs.month.bcd.value) - 1; + + /* tm_year years since 2000 */ + if (regs.month.bits.century) { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 200; + } else { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 100; + } + + /* tm_yday day of year [0,365] */ + time->tm_yday = 0; /* TODO */ + + /* tm_isdst daylight savings flag */ + time->tm_isdst = 0; /* TODO */ + + return ret; +} + +int MAX31328::irq_enable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_CONTROL, &val8); + if (ret) { + return ret; + } + + val8 |= MAX31328_F_CTRL_INTCN; + if (id == INTR_ID_ALL) { + val8 |= MAX31328_F_CTRL_A2IE | MAX31328_F_CTRL_A1IE; + } else { + val8 |= id; + } + + ret = write_register(MAX31328_R_CONTROL, &val8); + + return ret; +} + +int MAX31328::irq_disable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_CONTROL, &val8); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 &= ~MAX31328_F_CTRL_INTCN; + val8 &= ~(MAX31328_F_CTRL_A2IE | MAX31328_F_CTRL_A1IE); + } else { + val8 &= ~id; + } + + val8 &= ~id; + ret = write_register(MAX31328_R_CONTROL, &val8); + + return ret; +} + +int MAX31328::irq_clear_flag(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_STATUS, &val8); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 &= ~(MAX31328_F_STATUS_A2F | MAX31328_F_STATUS_A1F); + } else { + val8 &= ~id; + } + ret = write_register(MAX31328_R_STATUS, &val8); + + return ret; +} + +int MAX31328::set_square_wave_frequency(sqw_out_freq_t freq) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31328_R_CONTROL, &val8); + if (ret) { + return ret; + } + + // Set mode to SQW + val8 &= ~MAX31328_F_CTRL_INTCN; + + // Set Frequency + val8 &= ~MAX31328_F_CTRL_RS; + val8 |= SET_BIT_VAL(freq, MAX31328_F_CTRL_RS_POS, MAX31328_F_CTRL_RS); + + ret = write_register(MAX31328_R_CONTROL, &val8); + + return ret; +} + +int MAX31328::start_temp_conversion(void) +{ + int ret; + uint8_t cfg; + uint8_t stat; + + ret = read_register(MAX31328_R_STATUS, &stat); + if (ret) { + return ret; + } + + if (stat & MAX31328_F_STATUS_BSY) { + return MAX31328_ERR_BUSY; // Device is busy + } + + ret = read_register(MAX31328_R_CONTROL, &cfg); + if (ret) { + return ret; + } + + cfg |= MAX31328_F_CTRL_CONV; + + ret = write_register(MAX31328_R_CONTROL, &cfg); + + return ret; +} + +int MAX31328::is_temp_ready(void) +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31328_R_CONTROL, &cfg); + if (ret) { + return ret; + } + + if (cfg & MAX31328_F_CTRL_CONV) { + return MAX31328_ERR_BUSY; // means temperatrue NOT ready + } else { + return 0; // means temperature ready + } + + return ret; +} + +int MAX31328::get_temp(float &temp) +{ + int ret; + uint8_t buf[2]; + uint16_t count; + + #define TEMP_RESOLUTION_FOR_10_BIT (0.25f) + + ret = read_register(MAX31328_R_MSB_TEMP, buf, 2); + if (ret) { + return ret; + } + + // buf[0] includes upper 8 bits, buf[1](7:6 bits) includes lower 2 bits + count =(buf[0]<<2) | ( (buf[1]>>6) & 0x03 ); + + count &= 0x3FF; // Resolution is 10 bits, mask rest of it + + // convert count to temperature, 10th bit is sign bit + if (count & (1<<9) ) { + count = (count ^ 0x3FF) + 1; + temp = count * TEMP_RESOLUTION_FOR_10_BIT; + temp = 0 - temp; // convert to negative + } else { + temp = count * TEMP_RESOLUTION_FOR_10_BIT; + } + + return ret; +} diff --git a/src/MAX31328/MAX31328.h b/src/MAX31328/MAX31328.h new file mode 100644 index 0000000..8411af2 --- /dev/null +++ b/src/MAX31328/MAX31328.h @@ -0,0 +1,443 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + + +#ifndef _MAX31328_H_ +#define _MAX31328_H_ + + +#include + +#include "Arduino.h" +#include +#include + + +#define MAX31328_DRV_VERSION "v1.0.1" + +#define MAX31328_ERR_UNKNOWN (-1) +#define MAX31328_ERR_BUSY (-3) + + +class MAX31328 +{ + public: + typedef enum { + INTR_ID_ALARM1 = 1<<0, /**< Alarm1 flag */ + INTR_ID_ALARM2 = 1<<1, /**< Alarm2 flag */ + INTR_ID_ALL = 0xFF + } intr_id_t; + + typedef enum { + ALARM1 = 1<<0, // Alarm number 1 + ALARM2 = 1<<1, // Alarm number 2 + } alarm_no_t; + + /** + * @brief Alarm periodicity selection + */ + typedef enum { + ALARM_PERIOD_EVERYSECOND, /**< Once per second */ + ALARM_PERIOD_EVERYMINUTE, /**< Second match / Once per minute */ + ALARM_PERIOD_HOURLY, /**< Second and Minute match */ + ALARM_PERIOD_DAILY, /**< Hour, Minute and Second match*/ + ALARM_PERIOD_WEEKLY, /**< Day and Time match */ + ALARM_PERIOD_MONTHLY, /**< Date and Time match */ + } alarm_period_t; + + /** + * @brief Square wave output frequency selection on CLKOUT pin + */ + typedef enum { + SQW_OUT_FREQ_1HZ = 0, // 1Hz + SQW_OUT_FREQ_4KHZ = 1, // 4.098kHz + SQW_OUT_FREQ_8KHZ = 2, // 8.192kHz + SQW_OUT_FREQ_32KHZ = 3, // 32.768kHz + } sqw_out_freq_t; + + typedef union { + uint8_t raw; + struct { + uint8_t a1f : 1; + uint8_t a2f : 1; + uint8_t bsy : 1; + uint8_t en32kHz : 1; + uint8_t : 3; // not used + uint8_t osf : 1; + } bits; + } reg_status_t; + + typedef union { + uint8_t raw; + struct { + uint8_t a1ie : 1; + uint8_t a2ie : 1; + uint8_t intcn : 1; + uint8_t rs : 2; + uint8_t conv : 1; + uint8_t bbsqw : 1; + uint8_t eosc : 1; + } bits; + } reg_cfg_t; + + + MAX31328(TwoWire *i2c, uint8_t i2c_addr=MAX3128_I2C_ADDRESS); + + /** + * @brief First initialization, must be call before using class function + * + */ + void begin(void); + + /** + * @brief Read status byte + * + * @param[out] stat: Decoded status byte + * + * @returns 0 on success, negative error code on failure. + */ + int get_status(reg_status_t &stat); + + /** + * @brief Set status byte + * + * @param[in] stat: Decoded status byte + * + * @returns 0 on success, negative error code on failure. + */ + int set_status(reg_status_t stat); + + /** + * @brief Get control byte + * + * @param[out] cfg: Decoded configuration byte + * + * @returns 0 on success, negative error code on failure. + */ + int get_configuration(reg_cfg_t &cfg); + + /** + * @brief Set control byte + * + * @param[in] cfg: Decoded configuration byte + * + * @returns 0 on success, negative error code on failure. + */ + int set_configuration(reg_cfg_t cfg); + + /** + * @brief Read time info from RTC. + * + * @param[out] rtc_time Time info from RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int get_time(struct tm *rtc_ctime); + + /** + * @brief Set time info to RTC. + * + * @param[in] rtc_time Time info to be written to RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int set_time(const struct tm *rtc_ctime); + + /** + * @brief Set an alarm condition + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[in] alarm_time Pointer to alarm time to be set + * @param[in] period Alarm periodicity, one of ALARM_PERIOD_* + * + * @return 0 on success, error code on failure + */ + int set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period); + + /** + * @brief Get alarm data & time + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[out] alarm_time Pointer to alarm time to be filled in + * @param[out] period Pointer to the period of alarm, one of ALARM_PERIOD_* + * @param[out] is_enabled Pointer to the state of alarm + * + * @return 0 on success, error code on failure + */ + int get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled); + + /** + * @brief Enable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_enable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Disable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_disable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Clear the interrupt flag + * + * @return 0 on success, error code on failure + */ + int irq_clear_flag(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Set square wave output frequency selection + * + * @param[in] freq Clock frequency, one of SQW_OUT_FREQ_* + * + * @return 0 on success, error code on failure + */ + int set_square_wave_frequency(sqw_out_freq_t freq); + + /** + * @brief Trigger temperature conversion + * + * @return 0 on success, error code on failure + */ + int start_temp_conversion(void); + + /** + * @brief To decide temperature conversion finished or not + * + * @return 0 on TempReady, MAX31328_ERR_BUSY on not ready, error code on failure + */ + int is_temp_ready(void); + + /** + * @brief Read temperature + * + * @param[in] temp, temperature value will be put inside it + * + * @return 0 on success, error code on failure + */ + int get_temp(float &temp); + + /** + * @brief Directly read value from register + * + * @param[in] reg: Register address + * @param[in] buf: destionation that values will be written + * @param[in] len: expected read len + * + * @return 0 on success, error code on failure + */ + int read_register(uint8_t reg, uint8_t *buf, uint8_t len=1); + + /** + * @brief Directly write value to register + * + * @param[in] reg: Register address + * @param[in] buf: values that would like to be written + * @param[in] len: buf len + * + * @return 0 on success, error code on failure + */ + int write_register(uint8_t reg, const uint8_t *buf, uint8_t len=1); + + private: + typedef struct { + union { + uint8_t raw; + struct { + uint8_t seconds : 4; // RTC seconds value + uint8_t sec_10 : 3; // RTC seconds in multiples of 10 + uint8_t : 1; + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } seconds; + + union { + uint8_t raw; + struct { + uint8_t minutes : 4; // RTC minutes value + uint8_t min_10 : 3; // RTC minutes in multiples of 10 + uint8_t : 1; + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } minutes; + + union { + uint8_t raw; + struct { + uint8_t hour : 4; // RTC hours value + uint8_t hr_10 : 1; // RTC hours in multiples of 10 + uint8_t hr20_am_pm : 1; // 0x0: Indicates AM in 12-hr format, 0x1: Indicates PM in 12-hr format + uint8_t f24_12 : 1; // 0x0: 24-hr format, 0x1: 12-hr format + uint8_t : 1; + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd_format24; + struct { + uint8_t value : 5; + uint8_t : 3; + } bcd_format12; + } hours; + + union { + uint8_t raw; + struct { + uint8_t day : 3; // RTC days + uint8_t : 5; + } bits; + struct { + uint8_t value : 3; + uint8_t : 5; + } bcd; + } day; + + union { + uint8_t raw; + struct { + uint8_t date : 4; // RTC date + uint8_t date_10 : 2; // RTC date in multiples of 10 + uint8_t : 2; + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd; + } date; + + union { + uint8_t raw; + struct { + uint8_t month : 4; // RTC months + uint8_t month_10 : 1; // RTC month in multiples of 10 + uint8_t : 2; + uint8_t century : 1; // 0x0: Year is in current century, 0x1: Year is in the next century + } bits; + struct { + uint8_t value : 5; + uint8_t : 3; + } bcd; + } month; + + union { + uint8_t raw; + struct { + uint8_t year : 4; // RTC years + uint8_t year_10 : 4; // RTC year multiples of 10 + } bits; + struct { + uint8_t value : 8; + } bcd; + } year; + } regs_rtc_time_t; + + typedef struct { + union { + uint8_t raw; + struct { + uint8_t seconds : 4; // Alarm1 seconds + uint8_t sec_10 : 3; // Alarm1 seconds in multiples of 10 + uint8_t axm1 : 1; // Alarm1 mask bit for minutes + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } sec; + + union { + uint8_t raw; + struct { + uint8_t minutes : 4; // Alarm1 minutes + uint8_t min_10 : 3; // Alarm1 minutes in multiples of 10 + uint8_t axm2 : 1; // Alarm1 mask bit for minutes + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } min; + + union { + uint8_t raw; + struct { + uint8_t hour : 4; // Alarm1 hours + uint8_t hr_10 : 1; // Alarm1 hours in multiples of 10 + uint8_t hr20_am_pm : 1; // 0x0: Indicates AM in 12-hr format, 0x1: Indicates PM in 12-hr format + uint8_t f24_12 : 1; // 0x0: 24-hr format, 0x1: 12-hr format + uint8_t axm3 : 1; // Alarm1 mask bit for hours + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd_format24; + struct { + uint8_t value : 5; + uint8_t : 3; + } bcd_format12; + } hrs; + + union { + uint8_t raw; + struct { + uint8_t day_date : 4; // Alarm1 day/date + uint8_t date_10 : 2; // Alarm1 date in multiples of 10 + uint8_t dy_dt : 1; + uint8_t axm4 : 1; // Alarm1 mask bit for day/date + } bits; + struct { + uint8_t value : 3; + uint8_t : 5; + } bcd_day; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd_date; + } day_date; + } regs_alarm_t; + + TwoWire *m_i2c; + uint8_t m_slave_addr; +}; +#endif /* _MAX31328_H_ */ diff --git a/src/MAX31328/MAX31328_registers.h b/src/MAX31328/MAX31328_registers.h new file mode 100644 index 0000000..6d1ac35 --- /dev/null +++ b/src/MAX31328/MAX31328_registers.h @@ -0,0 +1,103 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + + +#ifndef _MAX31328_REGISTERS_H_ +#define _MAX31328_REGISTERS_H_ + + +#define MAX3128_I2C_ADDRESS 0x68 + +#define MAX31328_R_SECONDS ( 0x00 ) +#define MAX31328_R_MINUTES ( 0x01 ) +#define MAX31328_R_HOURS ( 0x02 ) +#define MAX31328_R_DAY ( 0x03 ) +#define MAX31328_R_DATE ( 0x04 ) +#define MAX31328_R_MONTH ( 0x05 ) +#define MAX31328_R_YEAR ( 0x06 ) +#define MAX31328_R_ALRM1_SECONDS ( 0x07 ) +#define MAX31328_R_ALRM1_MINUTES ( 0x08 ) +#define MAX31328_R_ALRM1_HOURS ( 0x09 ) +#define MAX31328_R_ALRM1_DAY_DATE ( 0x0A ) +#define MAX31328_R_ALRM2_MINUTES ( 0x0B ) +#define MAX31328_R_ALRM2_HOURS ( 0x0C ) +#define MAX31328_R_ALRM2_DAY_DATE ( 0x0D ) +#define MAX31328_R_CONTROL ( 0x0E ) +#define MAX31328_R_STATUS ( 0x0F ) +#define MAX31328_R_AGING_OFFSET ( 0x10 ) +#define MAX31328_R_MSB_TEMP ( 0x11 ) +#define MAX31328_R_LSB_TEMP ( 0x12 ) + + +//control register bit masks +#define MAX31328_F_CTRL_A1IE_POS 0 +#define MAX31328_F_CTRL_A1IE (1<> 4) * 10) +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) +#define SWAPBYTES(val) (((val & 0xFF) << 8) | ((val & 0xFF00) >> 8)) + +#define pr_err(msg) Serial.println("max3133x.cpp: " msg) + +MAX3133X::MAX3133X(const reg_addr_t *reg_addr, TwoWire *i2c, uint8_t i2c_addr) +{ + if (i2c == NULL || reg_addr == NULL) + pr_err("i2c object is invalid!"); + + this->reg_addr = reg_addr; + i2c_handler = i2c; + slave_addr = i2c_addr; +} + +int MAX3133X::begin(void) +{ + int ret; + + if (i2c_handler == NULL || reg_addr == NULL) + return MAX3133X_NULL_VALUE_ERR; + + i2c_handler->begin(); + ret = sw_reset(); + if (ret != MAX3133X_NO_ERR) + return ret; + + ret = interrupt_disable(INT_ALL); + if (ret != MAX3133X_NO_ERR) + return ret; + + return oscillator_enable(); +} + +int MAX3133X::read_register(uint8_t reg, uint8_t *value, uint8_t len) +{ + int counter = 0; + + if (value == NULL) + return MAX3133X_NULL_VALUE_ERR; + + i2c_handler->beginTransmission(slave_addr); + + if (i2c_handler->write(reg) != 1) { + i2c_handler->flush(); + return MAX3133X_WRITE_REG_ERR; + } + + if (i2c_handler->endTransmission(false) != 0) { + i2c_handler->flush(); + return MAX3133X_I2C_END_TRANS_ERR; + } + + if (i2c_handler->requestFrom((uint8_t)slave_addr, len) != len) { + i2c_handler->flush(); + return MAX3133X_I2C_BUFF_ERR; + } + + while (i2c_handler->available()) { // slave may send less than requested + value[counter++] = i2c_handler->read(); // receive a byte as character + } + + if (counter != len) { + i2c_handler->flush(); + return MAX3133X_READ_REG_ERR; + } + + return MAX3133X_NO_ERR; +} + +int MAX3133X::write_register(uint8_t reg, const uint8_t *value, uint8_t len) +{ + if (value == NULL) + return MAX3133X_NULL_VALUE_ERR; + + i2c_handler->beginTransmission(slave_addr); + + if (i2c_handler->write(reg) != 1) { + i2c_handler->flush(); + return MAX3133X_WRITE_REG_ERR; + } + + if (i2c_handler->write(value, len) != len) { + i2c_handler->flush(); + return MAX3133X_WRITE_REG_ERR; + } + + if (i2c_handler->endTransmission() != 0) { + i2c_handler->flush(); + return MAX3133X_I2C_END_TRANS_ERR; + } + + return MAX3133X_NO_ERR; +} + +#define SET_BIT_FIELD(address, reg_name, bit_field_name, value) \ +{ int ret; \ + ret = read_register(address, (uint8_t *)&(reg_name), 1); \ + if (ret) \ + return ret; \ + if (bit_field_name != value){ \ + bit_field_name = value; \ + ret = write_register(address, (uint8_t *)&(reg_name), 1); \ + if (ret) \ + return ret; \ + } \ +} + +inline int8_t MAX3133X::hours_reg_to_hour(const max3133x_hours_reg_t *hours_reg) +{ + hour_format_t format = hours_reg->bits_24hr.f_24_12 == 1 ? HOUR12 : HOUR24; + if (format == HOUR24) { + return BCD2BIN(hours_reg->bcd_24hr.value); + } else { + if (hours_reg->bits_12hr.am_pm) { + if (hours_reg->bcd_12hr.value < 12) + return (hours_reg->bcd_12hr.value + 12); + } else { + if (hours_reg->bcd_12hr.value == 12) + return (hours_reg->bcd_12hr.value - 12); + } + } +} + +inline void MAX3133X::rtc_regs_to_time(struct tm *time, const max3133x_rtc_time_regs_t *regs, uint16_t *sub_sec) +{ + if (sub_sec != NULL) + *sub_sec = (1000 * regs->seconds_1_128_reg.raw) / 128.0; + + /* tm_sec seconds [0,61] */ + time->tm_sec = BCD2BIN(regs->seconds_reg.bcd.value); + + /* tm_min minutes [0,59] */ + time->tm_min = BCD2BIN(regs->minutes_reg.bcd.value); + + /* tm_hour hour [0,23] */ + time->tm_hour = hours_reg_to_hour(®s->hours_reg); + /*hour_format_t format = regs->hours_reg.bits_24hr.f_24_12 == 1 ? HOUR12 : HOUR24; + if (format == HOUR24) { + time->tm_hour = BCD2BIN(regs->hours_reg.bcd_24hr.value); + } else if (format == HOUR12) { + uint8_t hr24 = to_24hr(BCD2BIN(regs->hours_reg.bcd_12hr.value), regs->hours_reg.bits_12hr.am_pm); + time->tm_hour = hr24; + }*/ + + /* tm_wday day of week [0,6] (Sunday = 0) */ + time->tm_wday = BCD2BIN(regs->day_reg.bcd.value) - 1; + + /* tm_mday day of month [1,31] */ + time->tm_mday = BCD2BIN(regs->date_reg.bcd.value); + + /* tm_mon month of year [0,11] */ + time->tm_mon = BCD2BIN(regs->month_reg.bcd.value) - 1; + + /* tm_year years since 2000 */ + if (regs->month_reg.bits.century) + time->tm_year = BCD2BIN(regs->year_reg.bcd.value) + 200; + else + time->tm_year = BCD2BIN(regs->year_reg.bcd.value) + 100; + + /* tm_yday day of year [0,365] */ + time->tm_yday = 0; /* TODO */ + + /* tm_isdst daylight savings flag */ + time->tm_isdst = 0; /* TODO */ +} + +int MAX3133X::time_to_rtc_regs(max3133x_rtc_time_regs_t *regs, const struct tm *time, hour_format_t format) +{ + /********************************************************* + * +----------+------+---------------------------+-------+ + * | Member | Type | Meaning | Range | + * +----------+------+---------------------------+-------+ + * | tm_sec | int | seconds after the minute | 0-61* | + * | tm_min | int | minutes after the hour | 0-59 | + * | tm_hour | int | hours since midnight | 0-23 | + * | tm_mday | int | day of the month | 1-31 | + * | tm_mon | int | months since January | 0-11 | + * | tm_year | int | years since 1900 | | + * | tm_wday | int | days since Sunday | 0-6 | + * | tm_yday | int | days since January 1 | 0-365 | + * | tm_isdst | int | Daylight Saving Time flag | | + * +----------+------+---------------------------+-------+ + * * tm_sec is generally 0-59. The extra range is to accommodate for leap + * seconds in certain systems. + *********************************************************/ + regs->seconds_reg.bcd.value = BIN2BCD(time->tm_sec); + + regs->minutes_reg.bcd.value = BIN2BCD(time->tm_min); + + if (format == HOUR24) { + regs->hours_reg.bcd_24hr.value = BIN2BCD(time->tm_hour); + regs->hours_reg.bits_24hr.f_24_12 = HOUR24; + } else if (format == HOUR12) { + uint8_t hr_12, pm; + to_12hr(time->tm_hour, &hr_12, &pm); + regs->hours_reg.bcd_12hr.value = BIN2BCD(hr_12); + regs->hours_reg.bits_12hr.f_24_12 = HOUR12; + regs->hours_reg.bits_12hr.am_pm = pm; + } else { + pr_err("Invalid Hour Format!"); + return MAX3133X_INVALID_TIME_ERR; + } + + regs->day_reg.bcd.value = BIN2BCD(time->tm_wday + 1); + + regs->date_reg.bcd.value = BIN2BCD(time->tm_mday); + + regs->month_reg.bcd.value = BIN2BCD(time->tm_mon + 1); + + if (time->tm_year >= 200) { + regs->month_reg.bits.century = 1; + regs->year_reg.bcd.value = BIN2BCD(time->tm_year - 200); + } else if (time->tm_year >= 100) { + regs->month_reg.bits.century = 0; + regs->year_reg.bcd.value = BIN2BCD(time->tm_year - 100); + } else { + pr_err("Invalid set date!"); + return MAX3133X_INVALID_DATE_ERR; + } + + return MAX3133X_NO_ERR; +} + +int MAX3133X::get_time(struct tm *time, uint16_t *sub_sec) +{ + int ret; + max3133x_rtc_time_regs_t max3133x_rtc_time_regs; + if (time == NULL) { + pr_err("time is invalid!"); + return MAX3133X_NULL_VALUE_ERR; + } + + ret = read_register(reg_addr->seconds_1_128_reg_addr, + (uint8_t *) &max3133x_rtc_time_regs.seconds_1_128_reg, + sizeof(max3133x_rtc_time_regs)); + if (ret != MAX3133X_NO_ERR) { + pr_err("read time registers failed!"); + return ret; + } + + rtc_regs_to_time(time, &max3133x_rtc_time_regs, sub_sec); + return MAX3133X_NO_ERR; +} + +int MAX3133X::set_time(const struct tm *time, hour_format_t format) +{ + int ret; + max3133x_rtc_time_regs_t max3133x_rtc_time_regs; + + if (time == NULL) { + pr_err("time is invalid!"); + return MAX3133X_NULL_VALUE_ERR; + } + + ret = time_to_rtc_regs(&max3133x_rtc_time_regs, time, format); + if (ret != MAX3133X_NO_ERR) + return ret; + + return write_register(reg_addr->seconds_reg_addr, &max3133x_rtc_time_regs.seconds_reg.raw, sizeof(max3133x_rtc_time_regs)-1); +} + +inline void MAX3133X::timestamp_regs_to_time(timestamp_t *timestamp, const max3133x_ts_regs_t *timestamp_reg) +{ + /* tm_sec seconds [0,61] */ + timestamp->ctime.tm_sec = BCD2BIN(timestamp_reg->ts_sec_reg.bcd.value); + + /* tm_min minutes [0,59] */ + timestamp->ctime.tm_min = BCD2BIN(timestamp_reg->ts_min_reg.bcd.value); + + /* tm_hour hour [0,23] */ + timestamp->ctime.tm_hour = hours_reg_to_hour(×tamp_reg->ts_hour_reg); + /*hour_format_t format = timestamp_reg->ts_hour_reg.bits_24hr.f_24_12 ? HOUR12 : HOUR24; + if (format == HOUR24) { + timestamp->ctime.tm_hour = BCD2BIN(timestamp_reg->ts_hour_reg.bcd_24hr.value); + } else if (format == HOUR12) { + uint8_t hr24 = to_24hr(BCD2BIN(timestamp_reg->ts_hour_reg.bcd_12hr.value), timestamp_reg->ts_hour_reg.bits_12hr.am_pm); + timestamp->ctime.tm_hour = hr24; + }*/ + + /* tm_mday day of month [1,31] */ + timestamp->ctime.tm_mday = BCD2BIN(timestamp_reg->ts_date_reg.bcd.value); + + /* tm_mon month of year [0,11] */ + timestamp->ctime.tm_mon = BCD2BIN(timestamp_reg->ts_month_reg.bcd.value) - 1; + + /* tm_year years since 2000 */ + if (timestamp_reg->ts_month_reg.bits.century) + timestamp->ctime.tm_year = BCD2BIN(timestamp_reg->ts_year_reg.bcd.value) + 200; + else + timestamp->ctime.tm_year = BCD2BIN(timestamp_reg->ts_year_reg.bcd.value) + 100; + + /* tm_yday day of year [0,365] */ + timestamp->ctime.tm_yday = 0; /* TODO */ + + /* tm_isdst daylight savings flag */ + timestamp->ctime.tm_isdst = 0; /* TODO */ + + timestamp->sub_sec = (1000 * timestamp_reg->ts_sec_1_128_reg.raw) / 128.0; +} + +int MAX3133X::get_status_reg(max3133x_status_reg_t * status_reg) +{ + return read_register(reg_addr->status_reg_addr, &status_reg->raw, 1); +} + +int MAX3133X::get_interrupt_reg(max3133x_int_en_reg_t * int_en_reg) +{ + return read_register(reg_addr->int_en_reg_addr, &int_en_reg->raw, 1); +} + +int MAX3133X::interrupt_enable(uint8_t mask) +{ + int ret; + max3133x_int_en_reg_t int_en_reg; + + ret = read_register(reg_addr->int_en_reg_addr, &int_en_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + int_en_reg.raw |= mask; + return write_register(reg_addr->int_en_reg_addr, &int_en_reg.raw, 1); +} + +int MAX3133X::interrupt_disable(uint8_t mask) +{ + int ret; + max3133x_int_en_reg_t int_en_reg; + + ret = read_register(reg_addr->int_en_reg_addr, &int_en_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + int_en_reg.raw &= ~mask; + return write_register(reg_addr->int_en_reg_addr, &int_en_reg.raw, 1); +} + +int MAX3133X::sw_reset_assert() +{ + max3133x_rtc_reset_reg_t rtc_reset_reg; + SET_BIT_FIELD(reg_addr->rtc_reset_reg_addr, rtc_reset_reg, rtc_reset_reg.bits.swrst, 1); + return MAX3133X_NO_ERR; +} + +int MAX3133X::sw_reset_release() +{ + max3133x_rtc_reset_reg_t rtc_reset_reg; + SET_BIT_FIELD(reg_addr->rtc_reset_reg_addr, rtc_reset_reg, rtc_reset_reg.bits.swrst, 0); + return MAX3133X_NO_ERR; +} + +int MAX3133X::sw_reset() +{ + int ret; + ret = sw_reset_assert(); + if (ret != MAX3133X_NO_ERR) + return ret; + + delay(500); + return sw_reset_release(); +} + +int MAX31331::rtc_config(rtc_config_t *max31331_config) +{ + int ret; + max3133x_rtc_config1_reg_t rtc_config1_reg; + max31331_rtc_config2_reg_t rtc_config2_reg; + + rtc_config1_reg.bits.a1ac = max31331_config->a1ac; + rtc_config1_reg.bits.dip = max31331_config->dip; + rtc_config1_reg.bits.data_ret = max31331_config->data_ret; + rtc_config1_reg.bits.i2c_timeout = max31331_config->i2c_timeout; + rtc_config1_reg.bits.en_osc = max31331_config->en_osc; + + ret = write_register(reg_addr.rtc_config1_reg_addr, &rtc_config1_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + rtc_config2_reg.bits.clko_hz = max31331_config->clko_hz; + rtc_config2_reg.bits.enclko = max31331_config->enclko; + + return write_register(reg_addr.rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); +} + +int MAX31334::rtc_config(rtc_config_t *max31334_config) +{ + int ret; + max3133x_rtc_config1_reg_t rtc_config1_reg; + max31334_rtc_config2_reg_t rtc_config2_reg; + + rtc_config1_reg.bits.a1ac = max31334_config->a1ac; + rtc_config1_reg.bits.dip = max31334_config->dip; + rtc_config1_reg.bits.data_ret = max31334_config->data_ret; + rtc_config1_reg.bits.i2c_timeout = max31334_config->i2c_timeout; + rtc_config1_reg.bits.en_osc = max31334_config->en_osc; + + ret = write_register(reg_addr.rtc_config1_reg_addr, &rtc_config1_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + rtc_config2_reg.bits.clko_hz = max31334_config->clko_hz; + rtc_config2_reg.bits.enclko = max31334_config->enclko; + rtc_config2_reg.bits.ddb = max31334_config->ddb; + rtc_config2_reg.bits.dse = max31334_config->dse; + + return write_register(reg_addr.rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); +} + +int MAX31331::get_rtc_config(rtc_config_t *max31331_config) +{ + int ret; + max3133x_rtc_config1_reg_t rtc_config1_reg; + max31331_rtc_config2_reg_t rtc_config2_reg; + + ret = read_register(reg_addr.rtc_config1_reg_addr, &rtc_config1_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + max31331_config->a1ac = (a1ac_t)rtc_config1_reg.bits.a1ac; + max31331_config->dip = (dip_t)rtc_config1_reg.bits.dip; + max31331_config->data_ret = (data_ret_t)rtc_config1_reg.bits.data_ret; + max31331_config->i2c_timeout = (i2c_timeout_t)rtc_config1_reg.bits.i2c_timeout; + max31331_config->en_osc = (en_osc_t)rtc_config1_reg.bits.en_osc; + + ret = read_register(reg_addr.rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + max31331_config->clko_hz = (clko_hz_t)rtc_config2_reg.bits.clko_hz; + max31331_config->enclko = (enclko_t)rtc_config2_reg.bits.enclko; + return MAX3133X_NO_ERR; +} + +int MAX31334::get_rtc_config(rtc_config_t *max31334_config) +{ + int ret; + max3133x_rtc_config1_reg_t rtc_config1_reg; + max31334_rtc_config2_reg_t rtc_config2_reg; + + ret = read_register(reg_addr.rtc_config1_reg_addr, &rtc_config1_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + max31334_config->a1ac = (a1ac_t)rtc_config1_reg.bits.a1ac; + max31334_config->dip = (dip_t)rtc_config1_reg.bits.dip; + max31334_config->data_ret = (data_ret_t)rtc_config1_reg.bits.data_ret; + max31334_config->i2c_timeout = (i2c_timeout_t)rtc_config1_reg.bits.i2c_timeout; + max31334_config->en_osc = (en_osc_t)rtc_config1_reg.bits.en_osc; + + ret = read_register(reg_addr.rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + max31334_config->clko_hz = (clko_hz_t)rtc_config2_reg.bits.clko_hz; + max31334_config->ddb = (ddb_t)rtc_config2_reg.bits.ddb; + max31334_config->dse = (dse_t)rtc_config2_reg.bits.dse; + max31334_config->enclko = (enclko_t)rtc_config2_reg.bits.enclko; + + return MAX3133X_NO_ERR; +} + +int MAX3133X::set_alarm1_auto_clear(a1ac_t a1ac) +{ + max3133x_rtc_config1_reg_t rtc_config1_reg; + SET_BIT_FIELD(reg_addr->rtc_config1_reg_addr, rtc_config1_reg, rtc_config1_reg.bits.a1ac, a1ac); + return MAX3133X_NO_ERR; +} + +int MAX3133X::set_din_polarity(dip_t dip) +{ + max3133x_rtc_config1_reg_t rtc_config1_reg; + SET_BIT_FIELD(reg_addr->rtc_config1_reg_addr, rtc_config1_reg, rtc_config1_reg.bits.dip, dip); + return MAX3133X_NO_ERR; +} + +int MAX3133X::data_retention_mode_config(bool enable) +{ + max3133x_rtc_config1_reg_t rtc_config1_reg; + SET_BIT_FIELD(reg_addr->rtc_config1_reg_addr, rtc_config1_reg, rtc_config1_reg.bits.data_ret, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::data_retention_mode_enter() +{ + return data_retention_mode_config(1); +} + +int MAX3133X::data_retention_mode_exit() +{ + return data_retention_mode_config(0); +} + +int MAX3133X::i2c_timeout_config(bool enable) +{ + max3133x_rtc_config1_reg_t rtc_config1_reg; + SET_BIT_FIELD(reg_addr->rtc_config1_reg_addr, rtc_config1_reg, rtc_config1_reg.bits.i2c_timeout, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::i2c_timeout_enable() +{ + return i2c_timeout_config(1); +} + +int MAX3133X::i2c_timeout_disable() +{ + return i2c_timeout_config(0); +} + +int MAX3133X::oscillator_config(bool enable) +{ + max3133x_rtc_config1_reg_t rtc_config1_reg; + SET_BIT_FIELD(reg_addr->rtc_config1_reg_addr, rtc_config1_reg, rtc_config1_reg.bits.en_osc, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::oscillator_enable() +{ + return oscillator_config(1); +} + +int MAX3133X::oscillator_disable() +{ + return oscillator_config(0); +} + +int MAX31334::get_sleep_state() +{ + int ret; + max31334_rtc_config2_reg_t rtc_config2_reg; + + ret = read_register(reg_addr.rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + return rtc_config2_reg.bits.slst; +} + +int MAX31334::din_sleep_entry_config(bool enable) +{ + max31334_rtc_config2_reg_t rtc_config2_reg; + SET_BIT_FIELD(reg_addr.rtc_config2_reg_addr, rtc_config2_reg, rtc_config2_reg.bits.dse, enable); + return MAX3133X_NO_ERR; +} + +int MAX31334::din_sleep_entry_enable() +{ + return din_sleep_entry_config(1); +} + +int MAX31334::din_sleep_entry_disable() +{ + return din_sleep_entry_config(0); +} + +int MAX31334::din_pin_debounce_config(bool enable) +{ + max31334_rtc_config2_reg_t rtc_config2_reg; + SET_BIT_FIELD(reg_addr.rtc_config2_reg_addr, rtc_config2_reg, rtc_config2_reg.bits.ddb, enable); + return MAX3133X_NO_ERR; +} + +int MAX31334::din_pin_debounce_enable() +{ + return din_pin_debounce_config(1); +} + +int MAX31334::din_pin_debounce_disable() +{ + return din_pin_debounce_config(0); +} + +int MAX3133X::clkout_config(bool enable) +{ + max31334_rtc_config2_reg_t rtc_config2_reg; + SET_BIT_FIELD(reg_addr->rtc_config2_reg_addr, rtc_config2_reg, rtc_config2_reg.bits.enclko, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::clkout_enable() +{ + return clkout_config(1); +} + +int MAX3133X::clkout_disable() +{ + return clkout_config(0); +} + +int MAX3133X::set_clko_freq(clko_hz_t clko_hz) +{ + max31334_rtc_config2_reg_t rtc_config2_reg; + SET_BIT_FIELD(reg_addr->rtc_config2_reg_addr, rtc_config2_reg, rtc_config2_reg.bits.clko_hz, clko_hz); + return MAX3133X_NO_ERR; +} + +int MAX3133X::get_clko_freq(clko_hz_t *clko_hz) +{ + int ret; + max31334_rtc_config2_reg_t rtc_config2_reg; + + ret = read_register(reg_addr->rtc_config2_reg_addr, &rtc_config2_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + *clko_hz = (clko_hz_t)rtc_config2_reg.bits.clko_hz; + return MAX3133X_NO_ERR; +} + +int MAX3133X::timestamp_function_enable() +{ + max3133x_timestamp_config_reg_t timestamp_config_reg; + SET_BIT_FIELD(reg_addr->timestamp_config_reg_addr, timestamp_config_reg, timestamp_config_reg.bits.tse, 1); + return MAX3133X_NO_ERR; +} + +int MAX3133X::timestamp_function_disable() +{ + max3133x_timestamp_config_reg_t timestamp_config_reg; + SET_BIT_FIELD(reg_addr->timestamp_config_reg_addr, timestamp_config_reg, timestamp_config_reg.bits.tse, 0); + return MAX3133X_NO_ERR; +} + +int MAX3133X::timestamp_registers_reset() +{ + max3133x_timestamp_config_reg_t timestamp_config_reg; + SET_BIT_FIELD(reg_addr->timestamp_config_reg_addr, timestamp_config_reg, timestamp_config_reg.bits.tsr, 1); + return MAX3133X_NO_ERR; +} + +int MAX3133X::timestamp_overwrite_config(bool enable) +{ + max3133x_timestamp_config_reg_t timestamp_config_reg; + SET_BIT_FIELD(reg_addr->timestamp_config_reg_addr, timestamp_config_reg, timestamp_config_reg.bits.tsow, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::timestamp_overwrite_enable() +{ + return timestamp_overwrite_config(1); +} + +int MAX3133X::timestamp_overwrite_disable() +{ + return timestamp_overwrite_config(0); +} + +int MAX3133X::timestamp_record_enable(uint8_t record_enable_mask) +{ + int ret; + max3133x_timestamp_config_reg_t timestamp_config_reg; + + if (record_enable_mask > (TSVLOW | TSPWM | TSDIN)) + return MAX3133X_INVALID_MASK_ERR; + + ret = read_register(reg_addr->timestamp_config_reg_addr, ×tamp_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timestamp_config_reg.raw |= record_enable_mask; + return write_register(reg_addr->timestamp_config_reg_addr, ×tamp_config_reg.raw, 1); +} + +int MAX3133X::timestamp_record_disable(uint8_t record_disable_mask) +{ + int ret; + max3133x_timestamp_config_reg_t timestamp_config_reg; + + if (record_disable_mask > (TSVLOW | TSPWM | TSDIN)) + return MAX3133X_INVALID_MASK_ERR; + + ret = read_register(reg_addr->timestamp_config_reg_addr, ×tamp_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timestamp_config_reg.raw &= ~record_disable_mask; + return write_register(reg_addr->timestamp_config_reg_addr, ×tamp_config_reg.raw, 1); +} + +int MAX31331::timer_init(uint8_t timer_init, bool repeat, timer_freq_t freq) +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr.timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 0; /* timer is reset */ + timer_config_reg.bits.tpause = 1; /* timer is paused */ + timer_config_reg.bits.trpt = repeat ? 1 : 0; /* Timer repeat mode */ + timer_config_reg.bits.tfs = freq; /* Timer frequency */ + + ret = write_register(reg_addr.timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + return write_register(reg_addr.timer_init_reg_addr, &timer_init, 1); +} + +int MAX31334::timer_init(uint16_t timer_init, bool repeat, timer_freq_t freq) +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr.timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 0; /* timer is reset */ + timer_config_reg.bits.tpause = 1; /* timer is paused */ + timer_config_reg.bits.trpt = repeat ? 1 : 0; /* Timer repeat mode */ + timer_config_reg.bits.tfs = freq; /* Timer frequency */ + + ret = write_register(reg_addr.timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_init = SWAPBYTES(timer_init); + + return write_register(reg_addr.timer_init2_reg_addr, (uint8_t *)&timer_init, 2); +} + +int MAX31331::timer_get() +{ + int ret; + uint8_t timer_count; + + ret = read_register(reg_addr.timer_count_reg_addr, &timer_count, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + return timer_count; +} + +int MAX31334::timer_get() +{ + int ret; + uint16_t timer_count; + + ret = read_register(reg_addr.timer_count2_reg_addr, (uint8_t *)&timer_count, 2); + if (ret != MAX3133X_NO_ERR) + return ret; + + return SWAPBYTES(timer_count); +} + +int MAX3133X::timer_start() +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 1; + timer_config_reg.bits.tpause = 0; + + return write_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); +} + +int MAX3133X::timer_pause() +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 1; + timer_config_reg.bits.tpause = 1; + + return write_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); +} + +int MAX3133X::timer_continue() +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 1; + timer_config_reg.bits.tpause = 0; + + return write_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); +} + +int MAX3133X::timer_stop() +{ + int ret; + max3133x_timer_config_reg_t timer_config_reg; + + ret = read_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timer_config_reg.bits.te = 0; + timer_config_reg.bits.tpause = 1; + + return write_register(reg_addr->timer_config_reg_addr, &timer_config_reg.raw, 1); +} + +int MAX31334::sleep_enter() +{ + max31334_sleep_config_reg_t sleep_config_reg; + SET_BIT_FIELD(reg_addr.sleep_config_reg_addr, sleep_config_reg, sleep_config_reg.bits.slp, 1); + return MAX3133X_NO_ERR; +} + +int MAX31334::sleep_exit() +{ + max31334_sleep_config_reg_t sleep_config_reg; + SET_BIT_FIELD(reg_addr.sleep_config_reg_addr, sleep_config_reg, sleep_config_reg.bits.slp, 0); + return MAX3133X_NO_ERR; +} + +int MAX31334::set_wait_state_timeout(wsto_t wsto) +{ + max31334_sleep_config_reg_t sleep_config_reg; + SET_BIT_FIELD(reg_addr.sleep_config_reg_addr, sleep_config_reg, sleep_config_reg.bits.wsto, wsto); + return MAX3133X_NO_ERR; +} + +int MAX31334::get_wait_state_timeout(wsto_t* wsto) +{ + int ret; + max31334_sleep_config_reg_t sleep_config_reg; + + ret = read_register(reg_addr.sleep_config_reg_addr, &sleep_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + *wsto = (wsto_t)sleep_config_reg.bits.wsto; + return MAX3133X_NO_ERR; +} + +int MAX31334::wakeup_enable(uint8_t wakeup_enable_mask) +{ + int ret; + max31334_sleep_config_reg_t sleep_config_reg; + + ret = read_register(reg_addr.sleep_config_reg_addr, &sleep_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + sleep_config_reg.raw |= wakeup_enable_mask; + return write_register(reg_addr.sleep_config_reg_addr, &sleep_config_reg.raw, 1); +} + +int MAX31334::wakeup_disable(uint8_t wakeup_disable_mask) +{ + int ret; + max31334_sleep_config_reg_t sleep_config_reg; + + ret = read_register(reg_addr.sleep_config_reg_addr, &sleep_config_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + sleep_config_reg.raw &= ~wakeup_disable_mask; + return write_register(reg_addr.sleep_config_reg_addr, &sleep_config_reg.raw, 1); +} + +int MAX3133X::battery_voltage_detector_config(bool enable) +{ + max3133x_pwr_mgmt_reg_t pwr_mgmt_reg; + SET_BIT_FIELD(reg_addr->pwr_mgmt_reg_addr, pwr_mgmt_reg, pwr_mgmt_reg.bits.en_vbat_detect, enable); + return MAX3133X_NO_ERR; +} + +int MAX3133X::battery_voltage_detector_enable() +{ + return battery_voltage_detector_config(1); +} + +int MAX3133X::battery_voltage_detector_disable() +{ + return battery_voltage_detector_config(0); +} + +int MAX3133X::supply_select(power_mgmt_supply_t supply) +{ + int ret; + max3133x_pwr_mgmt_reg_t pwr_mgmt_reg; + + ret = read_register(reg_addr->pwr_mgmt_reg_addr, &pwr_mgmt_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + switch (supply) { + case POW_MGMT_SUPPLY_SEL_VCC: + pwr_mgmt_reg.bits.manual_sel = 1; + pwr_mgmt_reg.bits.vback_sel = 0; + break; + case POW_MGMT_SUPPLY_SEL_VBAT: + pwr_mgmt_reg.bits.manual_sel = 1; + pwr_mgmt_reg.bits.vback_sel = 1; + break; + case POW_MGMT_SUPPLY_SEL_AUTO: + default: + pwr_mgmt_reg.bits.manual_sel = 0; + break; + } + + return write_register(reg_addr->pwr_mgmt_reg_addr, &pwr_mgmt_reg.raw, 1); +} + +int MAX3133X::trickle_charger_enable(trickle_charger_ohm_t res, bool diode) +{ + max3133x_trickle_reg_reg_t trickle_reg_reg; + trickle_reg_reg.bits.trickle = res; + + if (diode) + trickle_reg_reg.bits.trickle |= 0x04; + + trickle_reg_reg.bits.en_trickle = true; + + return write_register(reg_addr->trickle_reg_addr, &trickle_reg_reg.raw, 1); +} + +int MAX3133X::trickle_charger_disable() +{ + max3133x_trickle_reg_reg_t trickle_reg_reg; + SET_BIT_FIELD(reg_addr->trickle_reg_addr, trickle_reg_reg, trickle_reg_reg.bits.en_trickle, 0); + return MAX3133X_NO_ERR; +} + +int MAX3133X::get_timestamp(int ts_num, timestamp_t *timestamp) +{ + int ret; + max3133x_ts_regs_t timestamp_reg; + max3133x_ts_flags_reg_t ts_flags_reg; + uint8_t ts_reg_addr; + uint8_t ts_flag_reg_addr = reg_addr->ts0_flags_reg_addr + sizeof(max3133x_ts_regs_t)*ts_num; + + ret = read_register(ts_flag_reg_addr, (uint8_t *)&ts_flags_reg, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timestamp->ts_num = (ts_num_t)ts_num; + timestamp->ts_trigger = (ts_trigger_t)(ts_flags_reg.raw & 0xF); + + if (ts_flags_reg.raw == NOT_TRIGGERED) + return ret; + + ts_reg_addr = reg_addr->ts0_sec_1_128_reg_addr + sizeof(max3133x_ts_regs_t)*ts_num; + ret = read_register(ts_reg_addr, (uint8_t *)×tamp_reg, sizeof(max3133x_ts_regs_t)-1); + if (ret != MAX3133X_NO_ERR) + return ret; + + timestamp_regs_to_time(timestamp, ×tamp_reg); + return MAX3133X_NO_ERR; +} + +int MAX3133X::offset_configuration(int meas) +{ + short int offset; + double acc = (meas - 32768)*30.5175; + + offset = (short int)(acc/0.477); + + return write_register(reg_addr->offset_high_reg_addr, (uint8_t *)&offset, 2); +} + +int MAX3133X::oscillator_flag_config(bool enable) +{ + max3133x_int_en_reg_t int_en_reg; + SET_BIT_FIELD(reg_addr->int_en_reg_addr, int_en_reg, int_en_reg.bits.dosf, !enable); + + return MAX3133X_NO_ERR; +} + +inline void MAX3133X::to_12hr(uint8_t hr, uint8_t *hr_12, uint8_t *pm) { + if (hr == 0) { + *hr_12 = 12; + *pm = 0; + } else if (hr < 12) { + *hr_12 = hr; + *pm = 0; + } else if (hr == 12) { + *hr_12 = 12; + *pm = 1; + } else { + *hr_12 = hr - 12; + *pm = 1; + } +} + +int MAX3133X::set_alarm_period(alarm_no_t alarm_no, max3133x_alarm_regs_t ®s, alarm_period_t period) +{ + regs.sec.bits.am1 = 1; + regs.min.bits.am2 = 1; + regs.hrs.bits_24hr.am3 = 1; + regs.day_date.bits.am4 = 1; + regs.mon.bits.am5 = 1; + regs.mon.bits.am6 = 1; + regs.day_date.bits.dy_dt_match = 1; + + switch (period) { + case ALARM_PERIOD_ONETIME: + if (alarm_no == ALARM2) { + pr_err("Alarm2 does not support onetime alarm"); + return MAX3133X_ALARM_ONETIME_NOT_SUPP_ERR; + } + regs.mon.bits.am6 = 0; + case ALARM_PERIOD_YEARLY: + if (alarm_no == ALARM2) { + pr_err("Alarm2 does not support once per year alarm"); + return MAX3133X_ALARM_YEARLY_NOT_SUPP_ERR; + } + regs.mon.bits.am5 = 0; + case ALARM_PERIOD_MONTHLY: + regs.day_date.bits.dy_dt_match = 0; + case ALARM_PERIOD_WEEKLY: + regs.day_date.bits.am4 = 0; + case ALARM_PERIOD_DAILY: + regs.hrs.bits_24hr.am3 = 0; + case ALARM_PERIOD_HOURLY: + regs.min.bits.am2 = 0; + case ALARM_PERIOD_EVERYMINUTE: + if ((alarm_no == ALARM2) && (period == ALARM_PERIOD_EVERYMINUTE)) { + pr_err("Alarm2 does not support every minute alarm"); + return MAX3133X_ALARM_EVERYMINUTE_NOT_SUPP_ERR; + } + regs.sec.bits.am1 = 0; + case ALARM_PERIOD_EVERYSECOND: + if ((alarm_no == ALARM2) && (period == ALARM_PERIOD_EVERYSECOND)) { + pr_err("Alarm2 does not support once per second alarm"); + return MAX3133X_ALARM_EVERYSECOND_NOT_SUPP_ERR; + } + break; + default: + pr_err("Invalid alarm period"); + return MAX3133X_INVALID_ALARM_PERIOD_ERR; + } + return MAX3133X_NO_ERR; +} + +int MAX3133X::time_to_alarm_regs(max3133x_alarm_regs_t ®s, const struct tm *alarm_time, hour_format_t format) +{ + regs.sec.bcd.value = BIN2BCD(alarm_time->tm_sec); + regs.min.bcd.value = BIN2BCD(alarm_time->tm_min); + + if (format == HOUR24) { + regs.hrs.bcd_24hr.value = BIN2BCD(alarm_time->tm_hour); + } else if (format == HOUR12) { + uint8_t hr_12, pm; + to_12hr(alarm_time->tm_hour, &hr_12, &pm); + regs.hrs.bcd_12hr.value = BIN2BCD(hr_12); + regs.hrs.bits_12hr.am_pm = pm; + } else { + pr_err("Invalid Hour Format!"); + return MAX3133X_INVALID_TIME_ERR; + } + + if (regs.day_date.bits.dy_dt_match == 0) /* Date match */ + regs.day_date.bcd_date.value = BIN2BCD(alarm_time->tm_mday); + else /* Day match */ + regs.day_date.bcd_day.value = BIN2BCD(alarm_time->tm_wday); + + regs.mon.bcd.value = BIN2BCD(alarm_time->tm_mon + 1); + + if (alarm_time->tm_year >= 200) { + regs.year.bcd.value = BIN2BCD(alarm_time->tm_year - 200); + } else if (alarm_time->tm_year >= 100) { + regs.year.bcd.value = BIN2BCD(alarm_time->tm_year - 100); + } else { + pr_err("Invalid set year!"); + return MAX3133X_INVALID_DATE_ERR; + } + + return MAX3133X_NO_ERR; +} + +int MAX3133X::set_alarm_regs(alarm_no_t alarm_no, const max3133x_alarm_regs_t *regs) +{ + uint8_t len = sizeof(max3133x_alarm_regs_t); + + if (alarm_no == ALARM1) + return write_register(reg_addr->alm1_sec_reg_addr, ®s->sec.raw, len); + else + return write_register(reg_addr->alm2_min_reg_addr, ®s->min.raw, len-3); +} + +int MAX3133X::get_rtc_time_format(hour_format_t *format) +{ + int ret; + max3133x_hours_reg_t hours_reg; + ret = read_register(reg_addr->hours_reg_addr, (uint8_t *)&hours_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + *format = (hour_format_t)hours_reg.bits_24hr.f_24_12; + + return MAX3133X_NO_ERR; +} + +int MAX3133X::set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period) +{ + int ret; + max3133x_alarm_regs_t alarm_regs; + hour_format_t format; + + ret = set_alarm_period(alarm_no, alarm_regs, period); + if (ret != MAX3133X_NO_ERR) + return ret; + + ret = get_rtc_time_format(&format); + if (ret != MAX3133X_NO_ERR) + return ret; + + /* Convert time structure to alarm registers */ + ret = time_to_alarm_regs(alarm_regs, alarm_time, format); + if (ret != MAX3133X_NO_ERR) + return ret; + + return set_alarm_regs(alarm_no, &alarm_regs); +} + +inline void MAX3133X::alarm_regs_to_time(alarm_no_t alarm_no, struct tm *alarm_time, + const max3133x_alarm_regs_t *regs, hour_format_t format) +{ + alarm_time->tm_min = BCD2BIN(regs->min.bcd.value); + + if (format == HOUR24) { + alarm_time->tm_hour = BCD2BIN(regs->hrs.bcd_24hr.value); + } else if (format == HOUR12) { + if (regs->hrs.bits_12hr.am_pm) { + if (BCD2BIN(regs->hrs.bcd_12hr.value) < 12) + alarm_time->tm_hour = BCD2BIN(regs->hrs.bcd_12hr.value) + 12; + } else { + if (BCD2BIN(regs->hrs.bcd_12hr.value) == 12) + alarm_time->tm_hour = BCD2BIN(regs->hrs.bcd_12hr.value) - 12; + } + } + + if (regs->day_date.bits.dy_dt_match == 0) { /* date */ + alarm_time->tm_mday = BCD2BIN(regs->day_date.bcd_date.value); + alarm_time->tm_wday = 0; + } else { /* day */ + alarm_time->tm_wday = BCD2BIN(regs->day_date.bcd_day.value); + alarm_time->tm_mday = 0; + } + + if (alarm_no == ALARM1) { + alarm_time->tm_sec = BCD2BIN(regs->sec.bcd.value); + alarm_time->tm_mon = BCD2BIN(regs->mon.bcd.value) - 1; + alarm_time->tm_year = BCD2BIN(regs->year.bcd.value) + 100; /* XXX no century bit */ + } +} + +int MAX3133X::get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, + alarm_period_t *period, bool *is_enabled) +{ + int ret; + max3133x_alarm_regs_t alarm_regs; + max3133x_int_en_reg_t int_en_reg; + uint8_t len = sizeof(max3133x_alarm_regs_t); + hour_format_t format; + + ret = get_rtc_time_format(&format); + if (ret != MAX3133X_NO_ERR) + return ret; + + if (alarm_no == ALARM1) + ret = read_register(reg_addr->alm1_sec_reg_addr, &alarm_regs.sec.raw, len); + else + ret = read_register(reg_addr->alm2_min_reg_addr, &alarm_regs.min.raw, len-3); + + if (ret != MAX3133X_NO_ERR) + return ret; + + /* Convert alarm registers to time structure */ + alarm_regs_to_time(alarm_no, alarm_time, &alarm_regs, format); + + *period = ALARM_PERIOD_EVERYSECOND; + + if (alarm_no == ALARM1) { + if (alarm_regs.sec.bits.am1 == 0) + *period = ALARM_PERIOD_EVERYMINUTE; + } + + if (alarm_regs.min.bits.am2 == 0) + *period = ALARM_PERIOD_HOURLY; + if (alarm_regs.hrs.bits_24hr.am3 == 0) + *period = ALARM_PERIOD_DAILY; + if (alarm_regs.day_date.bits.am4 == 0) + *period = ALARM_PERIOD_WEEKLY; + if (alarm_regs.day_date.bits.dy_dt_match == 0) + *period = ALARM_PERIOD_MONTHLY; + if (alarm_no == ALARM1) { + if (alarm_regs.mon.bits.am5 == 0) *period = ALARM_PERIOD_YEARLY; + if (alarm_regs.mon.bits.am6 == 0) *period = ALARM_PERIOD_ONETIME; + } + + ret = read_register(reg_addr->int_en_reg_addr, (uint8_t *)&int_en_reg.raw, 1); + if (ret != MAX3133X_NO_ERR) + return ret; + + if (alarm_no == ALARM1) + *is_enabled = (int_en_reg.raw & (A1IE) == A1IE); + else + *is_enabled = (int_en_reg.raw & (A2IE) == A2IE); + + return MAX3133X_NO_ERR; +} + +const MAX3133X::reg_addr_t MAX31334::reg_addr = { +MAX31334_STATUS, +MAX31334_INT_EN, +MAX31334_RTC_RESET, +MAX31334_RTC_CONFIG1, +MAX31334_RTC_CONFIG2, +MAX31334_TIMESTAMP_CONFIG, +MAX31334_TIMER_CONFIG, +MAX31334_SLEEP_CONFIG, +MAX31334_SECONDS_1_128, +MAX31334_SECONDS, +MAX31334_MINUTES, +MAX31334_HOURS, +MAX31334_DAY, +MAX31334_DATE, +MAX31334_MONTH, +MAX31334_YEAR, +MAX31334_ALM1_SEC, +MAX31334_ALM1_MIN, +MAX31334_ALM1_HRS, +MAX31334_ALM1_DAY_DATE, +MAX31334_ALM1_MON, +MAX31334_ALM1_YEAR, +MAX31334_ALM2_MIN, +MAX31334_ALM2_HRS, +MAX31334_ALM2_DAY_DATE, +REG_NOT_AVAILABLE, +MAX31334_TIMER_COUNT2, +MAX31334_TIMER_COUNT1, +REG_NOT_AVAILABLE, +MAX31334_TIMER_INIT2, +MAX31334_TIMER_INIT1, +MAX31334_PWR_MGMT, +MAX31334_TRICKLE_REG, +MAX31334_OFFSET_HIGH, +MAX31334_OFFSET_LOW, +MAX31334_TS0_SEC_1_128, +MAX31334_TS0_SEC, +MAX31334_TS0_MIN, +MAX31334_TS0_HOUR, +MAX31334_TS0_DATE, +MAX31334_TS0_MONTH, +MAX31334_TS0_YEAR, +MAX31334_TS0_FLAGS, +MAX31334_TS1_SEC_1_128, +MAX31334_TS1_SEC, +MAX31334_TS1_MIN, +MAX31334_TS1_HOUR, +MAX31334_TS1_DATE, +MAX31334_TS1_MONTH, +MAX31334_TS1_YEAR, +MAX31334_TS1_FLAGS, +MAX31334_TS2_SEC_1_128, +MAX31334_TS2_SEC, +MAX31334_TS2_MIN, +MAX31334_TS2_HOUR, +MAX31334_TS2_DATE, +MAX31334_TS2_MONTH, +MAX31334_TS2_YEAR, +MAX31334_TS2_FLAGS, +MAX31334_TS3_SEC_1_128, +MAX31334_TS3_SEC, +MAX31334_TS3_MIN, +MAX31334_TS3_HOUR, +MAX31334_TS3_DATE, +MAX31334_TS3_MONTH, +MAX31334_TS3_YEAR, +MAX31334_TS3_FLAGS, +}; + +const MAX3133X::reg_addr_t MAX31331::reg_addr = { +MAX31331_STATUS, +MAX31331_INT_EN, +MAX31331_RTC_RESET, +MAX31331_RTC_CONFIG1, +MAX31331_RTC_CONFIG2, +MAX31331_TIMESTAMP_CONFIG, +MAX31331_TIMER_CONFIG, +REG_NOT_AVAILABLE, +MAX31331_SECONDS_1_128, +MAX31331_SECONDS, +MAX31331_MINUTES, +MAX31331_HOURS, +MAX31331_DAY, +MAX31331_DATE, +MAX31331_MONTH, +MAX31331_YEAR, +MAX31331_ALM1_SEC, +MAX31331_ALM1_MIN, +MAX31331_ALM1_HRS, +MAX31331_ALM1_DAY_DATE, +MAX31331_ALM1_MON, +MAX31331_ALM1_YEAR, +MAX31331_ALM2_MIN, +MAX31331_ALM2_HRS, +MAX31331_ALM2_DAY_DATE, +MAX31331_TIMER_COUNT, +REG_NOT_AVAILABLE, +REG_NOT_AVAILABLE, +MAX31331_TIMER_INIT, +REG_NOT_AVAILABLE, +REG_NOT_AVAILABLE, +MAX31331_PWR_MGMT, +MAX31331_TRICKLE_REG, +MAX31331_OFFSET_HIGH, +MAX31331_OFFSET_LOW, +MAX31331_TS0_SEC_1_128, +MAX31331_TS0_SEC, +MAX31331_TS0_MIN, +MAX31331_TS0_HOUR, +MAX31331_TS0_DATE, +MAX31331_TS0_MONTH, +MAX31331_TS0_YEAR, +MAX31331_TS0_FLAGS, +MAX31331_TS1_SEC_1_128, +MAX31331_TS1_SEC, +MAX31331_TS1_MIN, +MAX31331_TS1_HOUR, +MAX31331_TS1_DATE, +MAX31331_TS1_MONTH, +MAX31331_TS1_YEAR, +MAX31331_TS1_FLAGS, +MAX31331_TS2_SEC_1_128, +MAX31331_TS2_SEC, +MAX31331_TS2_MIN, +MAX31331_TS2_HOUR, +MAX31331_TS2_DATE, +MAX31331_TS2_MONTH, +MAX31331_TS2_YEAR, +MAX31331_TS2_FLAGS, +MAX31331_TS3_SEC_1_128, +MAX31331_TS3_SEC, +MAX31331_TS3_MIN, +MAX31331_TS3_HOUR, +MAX31331_TS3_DATE, +MAX31331_TS3_MONTH, +MAX31331_TS3_YEAR, +MAX31331_TS3_FLAGS, +}; \ No newline at end of file diff --git a/src/MAX3133X/MAX3133X.h b/src/MAX3133X/MAX3133X.h new file mode 100644 index 0000000..d15a0c0 --- /dev/null +++ b/src/MAX3133X/MAX3133X.h @@ -0,0 +1,1072 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef MAX3133X_HPP_ +#define MAX3133X_HPP_ + +#include +#include +#include +#include "MAX3133X_registers.h" + +enum max3133x_error_codes{ + MAX3133X_NO_ERR, + MAX3133X_NULL_VALUE_ERR = -1, + MAX3133X_READ_REG_ERR = -2, + MAX3133X_WRITE_REG_ERR = -3, + MAX3133X_INVALID_TIME_ERR = -4, + MAX3133X_INVALID_DATE_ERR = -5, + MAX3133X_INVALID_MASK_ERR = -6, + MAX3133X_INVALID_ALARM_PERIOD_ERR = -7, + MAX3133X_ALARM_ONETIME_NOT_SUPP_ERR = -8, + MAX3133X_ALARM_YEARLY_NOT_SUPP_ERR = -9, + MAX3133X_ALARM_EVERYMINUTE_NOT_SUPP_ERR = -10, + MAX3133X_ALARM_EVERYSECOND_NOT_SUPP_ERR = -11, + MAX3133X_I2C_BUFF_ERR = -12, + MAX3133X_I2C_END_TRANS_ERR = -13 +}; + +class MAX3133X +{ +public: + /* PUBLIC FUNCTION DECLARATIONS */ + + /** + * @brief Read from a register. + * + * @param[in] reg Address of a register to be read. + * @param[out] value Pointer to save result value. + * @param[in] len Size of result to be read. + * + * @returns 0 on success, negative error code on failure. + */ + int read_register(uint8_t reg, uint8_t *value, uint8_t len); + + /** + * @brief Write to a register. + * + * @param[in] reg Address of a register to be written. + * @param[out] value Pointer of value to be written to register. + * @param[in] len Size of result to be written. + * + * @returns 0 on success, negative error code on failure. + */ + int write_register(uint8_t reg, const uint8_t *value, uint8_t len); + + /** + * @brief First initialization, must be call before using class function + * + */ + int begin(void); + + /** + * @brief Read time info from RTC. + * + * @param[out] rtc_ctime Time info from RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int get_time(struct tm *rtc_ctime, uint16_t *sub_sec = NULL); + + /** + * @brief Selection of 24hr-12hr hour format + */ + typedef enum { + HOUR24 = 0, /**< 24-Hour format */ + HOUR12 = 1, /**< 12-Hour format */ + } hour_format_t; + + /** + * @brief Set time info to RTC. + * + * @param[in] rtc_ctime Time info to be written to RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int set_time(const struct tm *rtc_ctime, hour_format_t format = HOUR24); + + /** + * @brief Alarm periodicity selection + */ + typedef enum { + ALARM_PERIOD_EVERYSECOND, /**< Once a second */ + ALARM_PERIOD_EVERYMINUTE, /**< Seconds match */ + ALARM_PERIOD_HOURLY, /**< Seconds and Minutes match */ + ALARM_PERIOD_DAILY, /**< Hours, Minutes and Seconds match*/ + ALARM_PERIOD_WEEKLY, /**< Day and Time match */ + ALARM_PERIOD_MONTHLY, /**< Date and Time match */ + ALARM_PERIOD_YEARLY, /**< Month, Date and Time match */ + ALARM_PERIOD_ONETIME, /**< Year, Month, Date and Time match */ + } alarm_period_t; + + /** + * @brief Alarm number selection + */ + typedef enum { + ALARM1, /**< Alarm number 1 */ + ALARM2, /**< Alarm number 2 */ + } alarm_no_t; + + /** + * @brief Set an alarm condition + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[in] alarm_time Pointer to alarm time to be set + * @param[in] period Alarm periodicity, one of ALARM_PERIOD_* + * + * @return 0 on success, error code on failure + */ + int set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period); + + /** + * @brief Get alarm data & time + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[out] alarm_time Pointer to alarm time to be filled in + * @param[out] period Pointer to the period of alarm, one of ALARM_PERIOD_* + * @param[out] is_enabled Pointer to the state of alarm + * + * @return 0 on success, error code on failure + */ + int get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled); + + /** + * @brief Gets Status Register Value + * + * @param[in] status_reg + * + * @returns 0 on success, negative error code on failure. + */ + int get_status_reg(max3133x_status_reg_t * status_reg); + + /** + * @brief Gets Interrupt Enable Register Value + * + * @param[in] int_en_reg + * + * @returns 0 on success, negative error code on failure. + */ + int get_interrupt_reg(max3133x_int_en_reg_t * int_en_reg); + + /*Interrupt Enable Register Masks*/ + #define A1IE 0b00000001 /*Alarm1 interrupt mask*/ + #define A2IE 0b00000010 /*Alarm2 interrupt mask*/ + #define TIE 0b00000100 /*Timer interrupt mask*/ + #define DIE 0b00001000 /*Digital (DIN) interrupt mask*/ + #define VBATLOWIE 0b00010000 /*VBAT Low Interrupt mask*/ + #define PFAILE 0b00100000 /*Power fail Interrupt mask*/ + #define DOSF 0b01000000 /*Disable oscillator flag*/ + #define INT_ALL 0b01111111 /*All Interrupts*/ + #define NUM_OF_INT 6 /*Number of Interrupts*/ + + /** + * @brief Enables Interrupts + * + * @param[in] mask + * + * @returns 0 on success, negative error code on failure. + */ + int interrupt_enable(uint8_t mask); + + /** + * @brief Disables Interrupts + * + * @param[in] mask + * + * @returns 0 on success, negative error code on failure. + */ + int interrupt_disable(uint8_t mask); + + /** + * @brief Put device into reset state + * + * @return 0 on success, error code on failure + */ + int sw_reset_assert(); + + /** + * @brief Release device from state state + * + * @return 0 on success, error code on failure + */ + int sw_reset_release(); + + /** + * @brief Resets the digital block and the I2C programmable registers except for RAM registers and RTC_reset. + * + * @returns 0 on success, negative error code on failure. + */ + int sw_reset(); + + /** + * @brief Data retention mode enable/disable Configuration + * + * @details + * - Register : RTC_CONFIG1 + * - Bit Fields : [2] + * - Default : 0x0 + * - Description : Data retention mode enable/disable. + */ + typedef enum{ + NORMAL_OP_MODE, + DATA_RETENTION_MODE + }data_ret_t; + + /** + * @brief I2C timeout enable Configuration + * + * @details + * - Register : RTC_CONFIG1 + * - Bit Fields : [1] + * - Default : 0x1 + * - Description : I2C timeout enable + */ + typedef enum{ + DISABLE_I2C_TIMEOUT, + ENABLE_I2C_TIMEOUT + }i2c_timeout_t; + + /** + * @brief Active-high enable for the crystal oscillator Configuration + * + * @details + * - Register : RTC_CONFIG1 + * - Bit Fields : [0] + * - Default : 0x1 + * - Description : Active-high enable for the crystal oscillator + */ + typedef enum{ + DISABLE_OSCILLATOR, + ENABLE_OSCILLATOR + }en_osc_t; + + /** + * @brief Digital (DIN) pin Sleep Entry Enable Configuration + * + * @details + * - Register : RTC_CONFIG2 + * - Bit Fields : [4] + * - Default : 0x0 + * - Description : Digital (DIN) pin Sleep Entry Enable + */ + typedef enum{ + DIN_SLEEP_ENTRY_DISABLE, + DIN_SLEEP_ENTRY_ENABLE + }dse_t; + + /** + * @brief Digital (DIN) pin Debounce Enable Configuration + * + * @details + * - Register : RTC_CONFIG2 + * - Bit Fields : [3] + * - Default : 0x0 + * - Description : Digital (DIN) pin Debounce Enable + */ + typedef enum{ + DIN_DEBOUNCE_DISABLE, + DIN_DEBOUNCE_ENABLE + }ddb_t; + + /** + * @brief CLKOUT enable Configuration + * + * @details + * - Register : RTC_CONFIG2 + * - Bit Fields : [2] + * - Default : 0x0 + * - Description : CLKOUT enable + */ + typedef enum{ + INTERRUPT, + CLOCK_OUTPUT + }enclko_t; + + /** + * @brief Register Configuration + * + * @details + * - Register : RTC_CONFIG1 + * - Bit Fields : [5:4] + * - Default : 0x0 + * - Description : Alarm1 Auto Clear + */ + typedef enum{ + BY_READING, /**< 0x0: Alarm1 flag and interrupt can only be cleared by reading Status register via I2C */ + AFTER_10MS, /**< 0x1: Alarm1 flag and interrupt are cleared ~10ms after assertion */ + AFTER_500MS, /**< 0x2: Alarm1 flag and interrupt are cleared ~500ms after assertion */ + AFTER_5s /**< 0x3: Alarm1 flag and interrupt are cleared ~5s after assertion. This option should not be used when Alarm1 is set to OncePerSec. */ + }a1ac_t; + + /** + * @brief Sets Alarm1 Auto Clear Mode + * + * @param[in] a1ac A1AC bits. + * + * @returns 0 on success, negative error code on failure. + */ + int set_alarm1_auto_clear(a1ac_t a1ac); + + /** + * @brief Digital (DIN) interrupt polarity configuration + * + * @details + * - Register : RTC_CONFIG1 + * - Bit Fields : [3] + * - Default : 0x0 + * - Description : Digital (DIN) interrupt polarity + */ + typedef enum{ + FALLING_EDGE, /**< 0x0: Interrupt triggers on falling edge of DIN input. */ + RISING_EDGE /**< 0x1: Interrupt triggers on rising edge of DIN input. */ + }dip_t; + + /** + * @brief Digital (DIN) interrupt polarity + * + * @param[in] dip + * + * @returns 0 on success, negative error code on failure. + */ + int set_din_polarity(dip_t dip); + + /** + * @brief Put device into data retention mode + * + * @return 0 on success, error code on failure + */ + int data_retention_mode_enter(); + + /** + * @brief Remove device from data retention mode + * + * @return 0 on success, error code on failure + */ + int data_retention_mode_exit(); + + /** + * @brief Enable I2C timeout + * + * @return 0 on success, error code on failure + */ + int i2c_timeout_enable(); + + /** + * @brief Disable I2C timeout + * + * @return 0 on success, error code on failure + */ + int i2c_timeout_disable(); + + /** + * @brief Enable the crystal oscillator. + * + * @return 0 on success, error code on failure + */ + int oscillator_enable(); + + /** + * @brief Disable the crystal oscillator. + * + * @return 0 on success, error code on failure + */ + int oscillator_disable(); + + /** + * @brief Enable the CLKOUT. Sets INTBb/CLKOUT pin as CLKO (clock output). + * + * @return 0 on success, error code on failure + */ + int clkout_enable(); + + /** + * @brief Disable the CLKOUT. Sets INTBb/CLKOUT pin as INTBb (interrupt). + * + * @return 0 on success, error code on failure + */ + int clkout_disable(); + + /** + * @brief Set output clock frequency on INTBb/CLKOUT pin Configuration + * + * @details + * - Register : RTC_CONFIG2 + * - Bit Fields : [1:0] + * - Default : 0x3 + * - Description : Output clock frequency on INTBb/CLKOUT pin + */ + typedef enum{ + CLKOUT_1HZ, + CLKOUT_64HZ, + CLKOUT_1024KHZ, + CLKOUT_32KHZ_UNCOMP + }clko_hz_t; + + /** + * @brief Set output clock frequency on INTBb/CLKOUT pin + * + * @param[in] clko_hz + * + * @returns 0 on success, negative error code on failure. + */ + int set_clko_freq(clko_hz_t clko_hz); + + /** + * @brief Get output clock frequency on INTBb/CLKOUT pin + * + * @param[out] clko_hz + * + * @returns 0 on success, negative error code on failure. + */ + int get_clko_freq(clko_hz_t *clko_hz); + + /** + * @brief Enable the Timestamp Function + * + * @returns 0 on success, negative error code on failure. + */ + int timestamp_function_enable(); + + /** + * @brief Disable the Timestamp Function + * + * @returns 0 on success, negative error code on failure. + */ + int timestamp_function_disable(); + + /** + * @brief All Timestamp registers are reset to 0x00. If the Timestamp Function is Enabled, timestamp recording will start again. + * + * @returns 0 on success, negative error code on failure. + */ + int timestamp_registers_reset(); + + /** + * @brief Enable Timestamp Overwrite mode + * + * @details More than 4 timestamps are recorded by overwriting oldest timestamp. Latest timestamp is always stored in the TS0 bank; earliest timestamp will be stored in the TS3 bank. + * + * @return 0 on success, error code on failure + */ + int timestamp_overwrite_enable(); + + /** + * @brief Disable Timestamp Overwrite mode + * + * @details Timestamps are recorded (TS0 -> .. -> TS3). Latest timestamp is always stored in the TS0 bank. Further TS trigger events do not record timestamps. + * + * @return 0 on success, error code on failure + */ + int timestamp_overwrite_disable(); + + /*Timestamp Config Register Masks*/ + #define TSVLOW 0b00100000 /*Record Timestamp on VBATLOW detection */ + #define TSPWM 0b00010000 /*Record Timestamp on power supply switch (VCC <-> VBAT)*/ + #define TSDIN 0b00001000 /*Record Timestamp on DIN transition. Polarity controlled by DIP bitfield in RTC_Config1 register.*/ + + /** + * @brief Enable Timestamp Records + * + * @param[in] record_enable_mask one or more of TS* + * + * @returns 0 on success, negative error code on failure. + */ + int timestamp_record_enable(uint8_t record_enable_mask); + + /** + * @brief Disable Timestamp Records + * + * @param[in] record_disable_mask one or more of TS* + * + * @returns 0 on success, negative error code on failure. + */ + int timestamp_record_disable(uint8_t record_disable_mask); + + /** + * @brief Timer frequency selection Configuration + * + * @details + * - Register : TIMER_CONFIG + * - Bit Fields : [1:0] + * - Default : 0x0 + * - Description : Timer frequency selection + */ + typedef enum { + TIMER_FREQ_1024HZ, /**< 1024Hz */ + TIMER_FREQ_256HZ, /**< 256Hz */ + TIMER_FREQ_64HZ, /**< 64Hz */ + TIMER_FREQ_16HZ, /**< 16Hz */ + } timer_freq_t; + + /** + * @brief Enable timer + * + * @return 0 on success, error code on failure + */ + int timer_start(); + + /** + * @brief Pause timer, timer value is preserved + * + * @return 0 on success, error code on failure + */ + int timer_pause(); + + /** + * @brief Start timer from the paused value + * + * @return 0 on success, error code on failure + */ + int timer_continue(); + + /** + * @brief Disable timer + * + * @return 0 on success, error code on failure + */ + int timer_stop(); + + /** + * @brief Turn-on the Battery Voltage Detector Function + * + * @return 0 on success, error code on failure + */ + int battery_voltage_detector_enable(); + + /** + * @brief Turn-off the Battery Voltage Detector Function + * + * @return 0 on success, error code on failure + */ + int battery_voltage_detector_disable(); + + /** + * @brief Supply voltage select. + */ + typedef enum { + POW_MGMT_SUPPLY_SEL_AUTO, /**< Circuit decides whether to use VCC or VBACKUP */ + POW_MGMT_SUPPLY_SEL_VCC, /**< Use VCC as supply */ + POW_MGMT_SUPPLY_SEL_VBAT, /**< Use VBAT as supply */ + } power_mgmt_supply_t; + + /** + * @brief Select device power source + * + * @param[in] supply Supply selection, one of POW_MGMT_SUPPLY_SEL_* + * + * @return 0 on success, error code on failure + */ + int supply_select(power_mgmt_supply_t supply); + + /** + * @brief Selection of charging path's resistor value + */ + typedef enum { + TRICKLE_CHARGER_3K, /**< 3000 Ohm */ + TRICKLE_CHARGER_3K_2, /**< 3000 Ohm */ + TRICKLE_CHARGER_6K, /**< 6000 Ohm */ + TRICKLE_CHARGER_11K, /**< 11000 Ohm */ + } trickle_charger_ohm_t; + + /** + * @brief Configure trickle charger charging path, also enable it + * + * @param[in] res Value of resistor + * @param[in] diode Enable diode + * + * @return 0 on success, error code on failure + */ + int trickle_charger_enable(trickle_charger_ohm_t res, bool diode); + + /** + * @brief Disable Trickle Charger + * + * @return 0 on success, error code on failure + */ + int trickle_charger_disable(); + + /** + * @brief Selection of Timestamp + */ + typedef enum { + TS0, /**< Timestamp 0 */ + TS1, /**< Timestamp 1 */ + TS2, /**< Timestamp 2 */ + TS3, /**< Timestamp 3 */ + NUM_OF_TS /**< Number of Timestamps */ + } ts_num_t; + + /** + * @brief Timestamp Triggers + */ + typedef enum { + NOT_TRIGGERED, /**< Not Triggered */ + DINF, /**< triggered by DIN transition */ + VCCF, /**< triggered by VBAT -> VCC switch */ + VBATF, /**< triggered by VCC -> VBAT switch */ + VLOWF, /**< triggered by VLOW detection */ + NUM_OF_TRIG /**< Number of Triggers */ + } ts_trigger_t; + + typedef struct{ + ts_num_t ts_num; + ts_trigger_t ts_trigger; + uint16_t sub_sec; + struct tm ctime; + }timestamp_t; + + /** + * @brief Read Timestamp info. + * + * @param[in] ts_num Timestamp number. + * @param[out] timestamp Time info. + * + * @returns 0 on success, negative error code on failure. + */ + int get_timestamp(int ts_num, timestamp_t *timestamp); + + /** + * @brief correct the clock accuracy on your board. refer the datasheet for additional informations + * + * @param[in] meas Timestamp number. + * + * @returns 0 on success, negative error code on failure. + */ + int offset_configuration(int meas); + + /** + * @brief Allow the OSF to indicate the oscillator status. + * + * @returns 0 on success, negative error code on failure. + */ + int oscillator_flag_enable(); + + /** + * @brief Disable the oscillator flag, irrespective of the oscillator status + * + * @returns 0 on success, negative error code on failure. + */ + int oscillator_flag_disable(); + +protected: + typedef struct { + uint8_t status_reg_addr; + uint8_t int_en_reg_addr; + uint8_t rtc_reset_reg_addr; + uint8_t rtc_config1_reg_addr; + uint8_t rtc_config2_reg_addr; + uint8_t timestamp_config_reg_addr; + uint8_t timer_config_reg_addr; + uint8_t sleep_config_reg_addr; + uint8_t seconds_1_128_reg_addr; + uint8_t seconds_reg_addr; + uint8_t minutes_reg_addr; + uint8_t hours_reg_addr; + uint8_t day_reg_addr; + uint8_t date_reg_addr; + uint8_t month_reg_addr; + uint8_t year_reg_addr; + uint8_t alm1_sec_reg_addr; + uint8_t alm1_min_reg_addr; + uint8_t alm1_hrs_reg_addr; + uint8_t alm1_day_date_reg_addr; + uint8_t alm1_mon_reg_addr; + uint8_t alm1_year_reg_addr; + uint8_t alm2_min_reg_addr; + uint8_t alm2_hrs_reg_addr; + uint8_t alm2_day_date_reg_addr; + uint8_t timer_count_reg_addr; + uint8_t timer_count2_reg_addr; + uint8_t timer_count1_reg_addr; + uint8_t timer_init_reg_addr; + uint8_t timer_init2_reg_addr; + uint8_t timer_init1_reg_addr; + uint8_t pwr_mgmt_reg_addr; + uint8_t trickle_reg_addr; + uint8_t offset_high_reg_addr; + uint8_t offset_low_reg_addr; + uint8_t ts0_sec_1_128_reg_addr; + uint8_t ts0_sec_reg_addr; + uint8_t ts0_min_reg_addr; + uint8_t ts0_hour_reg_addr; + uint8_t ts0_date_reg_addr; + uint8_t ts0_month_reg_addr; + uint8_t ts0_year_reg_addr; + uint8_t ts0_flags_reg_addr; + uint8_t ts1_sec_1_128_reg_addr; + uint8_t ts1_sec_reg_addr; + uint8_t ts1_min_reg_addr; + uint8_t ts1_hour_reg_addr; + uint8_t ts1_date_reg_addr; + uint8_t ts1_month_reg_addr; + uint8_t ts1_year_reg_addr; + uint8_t ts1_flags_reg_addr; + uint8_t ts2_sec_1_128_reg_addr; + uint8_t ts2_sec_reg_addr; + uint8_t ts2_min_reg_addr; + uint8_t ts2_hour_reg_addr; + uint8_t ts2_date_reg_addr; + uint8_t ts2_month_reg_addr; + uint8_t ts2_year_reg_addr; + uint8_t ts2_flags_reg_addr; + uint8_t ts3_sec_1_128_reg_addr; + uint8_t ts3_sec_reg_addr; + uint8_t ts3_min_reg_addr; + uint8_t ts3_hour_reg_addr; + uint8_t ts3_date_reg_addr; + uint8_t ts3_month_reg_addr; + uint8_t ts3_year_reg_addr; + uint8_t ts3_flags_reg_addr; + } reg_addr_t; + + /* Constructors */ + MAX3133X(const reg_addr_t *reg_addr, TwoWire *i2c, uint8_t i2c_addr = MAX3133X_I2C_ADDRESS); + +private: + /* PRIVATE TYPE DECLARATIONS */ + + /* PRIVATE VARIABLE DECLARATIONS */ + TwoWire *i2c_handler; + + uint8_t slave_addr; + + /* PRIVATE CONSTANT VARIABLE DECLARATIONS */ + const reg_addr_t *reg_addr; + + /* PRIVATE FUNCTION DECLARATIONS */ + void rtc_regs_to_time(struct tm *time, const max3133x_rtc_time_regs_t *regs, uint16_t *sub_sec); + + int time_to_rtc_regs(max3133x_rtc_time_regs_t *regs, const struct tm *time, hour_format_t format); + + void timestamp_regs_to_time(timestamp_t *timestamp, const max3133x_ts_regs_t *timestamp_reg); + + int time_to_alarm_regs(max3133x_alarm_regs_t ®s, const struct tm *alarm_time, hour_format_t format); + + void alarm_regs_to_time(alarm_no_t alarm_no, struct tm *alarm_time, const max3133x_alarm_regs_t *regs, hour_format_t format); + + int set_alarm_period(alarm_no_t alarm_no, max3133x_alarm_regs_t ®s, alarm_period_t period); + + int set_alarm_regs(alarm_no_t alarm_no, const max3133x_alarm_regs_t *regs); + + void to_12hr(uint8_t hr, uint8_t *hr_12, uint8_t *pm); + + int get_rtc_time_format(hour_format_t *format); + + int data_retention_mode_config(bool enable); + + int battery_voltage_detector_config(bool enable); + + int clkout_config(bool enable); + + int i2c_timeout_config(bool enable); + + int oscillator_config(bool enable); + + int timestamp_overwrite_config(bool enable); + + int oscillator_flag_config(bool enable); + + int8_t hours_reg_to_hour(const max3133x_hours_reg_t *hours_reg); + + /** + * @brief Interrupt handler function + */ + void interrupt_handler(); + + /** + * @brief Post interrupt jobs after interrupt is detected. + */ + void post_interrupt_work(); + + struct handler { + void (*func)(void *); + void *cb; + }; + + handler interrupt_handler_list[NUM_OF_INT]; +}; + +/** MAX31334 Device Class +* +* Hold configurations for the MAX31334 +*/ +class MAX31334 : public MAX3133X +{ +private: + static const reg_addr_t reg_addr; + + int din_sleep_entry_config(bool enable); + + int din_pin_debounce_config(bool enable); + +public: + typedef struct{ + a1ac_t a1ac; /*RTC_CONFIG1 - Alarm1 Auto Clear */ + dip_t dip; /*RTC_CONFIG1 - Digital (DIN) interrupt polarity */ + data_ret_t data_ret; /*RTC_CONFIG1 - Data retention mode enable/disable. */ + i2c_timeout_t i2c_timeout;/*RTC_CONFIG1 - I2C timeout enable */ + en_osc_t en_osc; /*RTC_CONFIG1 - Active-high enable for the crystal oscillator */ + dse_t dse; /*RTC_CONFIG2 - Digital (DIN) pin Sleep Entry Enable */ + ddb_t ddb; /*RTC_CONFIG2 - Digital (DIN) pin Debounce Enable */ + enclko_t enclko; /*RTC_CONFIG2 - CLKOUT enable */ + clko_hz_t clko_hz; /*RTC_CONFIG2 - Set output clock frequency on INTBb/CLKOUT pin */ + }rtc_config_t; + + /** + * @brief Configure the device + * + * @param[in] max31334_config Device configuration + * + * @return 0 on success, error code on failure + * + * @note RTC_CONFIG1 and RTC_CONFIG2 registers are set. + */ + int rtc_config(rtc_config_t *max31334_config); + + /** + * @brief Get device configuration + * + * @param[out] max31334_config Device configuration + * + * @return 0 on success, error code on failure + * + * @note RTC_CONFIG1 and RTC_CONFIG2 register values are read. + */ + int get_rtc_config(rtc_config_t *max31334_config); + + /** + * @brief Initialize timer + * + * @param[in] init_val Timer initial value + * @param[in] repeat Timer repeat mode enable/disable + * @param[in] freq Timer frequency, one of TIMER_FREQ_* + * + * @return 0 on success, error code on failure + */ + int timer_init(uint16_t init_val, bool repeat, timer_freq_t freq); + + /** + * @brief Read timer value + * + * @return timer value on success, error code on failure + */ + int timer_get(); + + /** + * @brief Get Sleep State + * + * @return Sleep State. 0: SLST=0 indicates the PSW SM is not in Sleep state, + * 1: SLST=1 indicates the PSW SM is in Sleep state. + * negative: on failure + */ + int get_sleep_state(); + + /** + * @brief Enable the Digital (DIN) pin Sleep Entry + * + * @details DIN pin can be used to enter sleep state. + * + * @return 0 on success, error code on failure + */ + int din_sleep_entry_enable(); + + /** + * @brief Disable the Digital (DIN) pin Sleep Entry + * + * @details DIN pin cannot be used to enter sleep state (Sleep state entry is only possible by writing SLP=1 over I2C). + * + * @return 0 on success, error code on failure + */ + int din_sleep_entry_disable(); + + /** + * @brief Enable the Digital (DIN) pin Debounce function + * + * @details 50ms debounce on DIN pin enabled. + * + * @return 0 on success, error code on failure + */ + int din_pin_debounce_enable(); + + /** + * @brief Disable the Digital (DIN) pin Debounce function + * + * @details No debounce on DIN pin. + * + * @return 0 on success, error code on failure + */ + int din_pin_debounce_disable(); + + /** + * @brief Put PSW SM into Sleep state. + * + * @return 0 on success, error code on failure + */ + int sleep_enter(); + + /** + * @brief Put PSW SM into Active state. + * + * @return 0 on success, error code on failure + */ + int sleep_exit(); + + /** + * @brief Wait State Timeout Configuration + * + * @details + * - Register : SLEEP_CONFIG + * - Bit Fields : [6:4] + * - Default : 0x0 + * - Description : Wait State Timeout. This bitfield must be set before writing SLP=1 if a finite wait state duration is + * desired before entering the sleep state. + */ + typedef enum { + WSTO_0MS, /**< 0ms */ + WSTO_8MS, /**< 8ms */ + WSTO_16MS, /**< 16ms */ + WSTO_24MS, /**< 24ms */ + WSTO_32MS, /**< 32ms */ + WSTO_40MS, /**< 40ms */ + WSTO_48MS, /**< 48ms */ + WSTO_56MS /**< 56ms */ + } wsto_t; + + /** + * @brief Set Wait State Timeout + * + * @param[in] wsto Wait State Timeout + * + * @return 0 on success, error code on failure + */ + int set_wait_state_timeout(wsto_t wsto); + + /** + * @brief Get Wait State Timeout + * + * @param[out] wsto Wait State Timeout + * + * @return 0 on success, error code on failure + */ + int get_wait_state_timeout(wsto_t* wsto); + + /*Sleep Config Register Masks*/ + #define A1WE 0b00000001 /*Alarm1 Wakeup Enable */ + #define A2WE 0b00000010 /*Alarm2 Wakeup Enable */ + #define TWE 0b00000100 /*Timer Wakeup Enable */ + #define DWE 0b00001000 /*DIN Wakeup Enable */ + + /** + * @brief Enable Wakeup + * + * @param[in] wakeup_enable_mask one or more of Sleep Config Register Masks + * + * @returns 0 on success, negative error code on failure. + */ + int wakeup_enable(uint8_t wakeup_enable_mask); + + /** + * @brief Disable Wakeup + * + * @param[in] wakeup_disable_mask one or more of Sleep Config Register Masks + * + * @returns 0 on success, negative error code on failure. + */ + int wakeup_disable(uint8_t wakeup_disable_mask); + + MAX31334(TwoWire *i2c, uint8_t i2c_addr = MAX3133X_I2C_ADDRESS) : MAX3133X(®_addr, i2c, i2c_addr) {} +}; + +/** MAX31331 Device Class +* +* Hold configurations for the MAX31331 +*/ +class MAX31331 : public MAX3133X +{ +private: + static const reg_addr_t reg_addr; + +public: + typedef struct{ + a1ac_t a1ac; /*RTC_CONFIG1 - Alarm1 Auto Clear */ + dip_t dip; /*RTC_CONFIG1 - Digital (DIN) interrupt polarity */ + data_ret_t data_ret; /*RTC_CONFIG1 - Data retention mode enable/disable. */ + i2c_timeout_t i2c_timeout;/*RTC_CONFIG1 - I2C timeout enable */ + en_osc_t en_osc; /*RTC_CONFIG1 - Active-high enable for the crystal oscillator */ + enclko_t enclko; /*RTC_CONFIG2 - CLKOUT enable */ + clko_hz_t clko_hz; /*RTC_CONFIG2 - Set output clock frequency on INTBb/CLKOUT pin */ + }rtc_config_t; + + /** + * @brief Configure the device + * + * @param[in] max31331_config Device configuration + * + * @return 0 on success, error code on failure + * + * @note RTC_CONFIG1 and RTC_CONFIG2 registers are set. + */ + int rtc_config(rtc_config_t *max31331_config); + + /** + * @brief Get device configuration + * + * @param[out] max31331_config Device configuration + * + * @return 0 on success, error code on failure + * + * @note RTC_CONFIG1 and RTC_CONFIG2 register values are read. + */ + int get_rtc_config(rtc_config_t *max31331_config); + + /** + * @brief Initialize timer + * + * @param[in] init_val Timer initial value + * @param[in] repeat Timer repeat mode enable/disable + * @param[in] freq Timer frequency, one of TIMER_FREQ_* + * + * @return 0 on success, error code on failure + */ + int timer_init(uint8_t init_val, bool repeat, timer_freq_t freq); + + /** + * @brief Read timer value + * + * @return timer value on success, error code on failure + */ + int timer_get(); + + MAX31331(TwoWire *i2c, uint8_t i2c_addr = MAX3133X_I2C_ADDRESS) : MAX3133X(®_addr, i2c, i2c_addr) {} +}; + +#endif /* MAX3133X_HPP_ */ \ No newline at end of file diff --git a/src/MAX3133X/MAX3133X_registers.h b/src/MAX3133X/MAX3133X_registers.h new file mode 100644 index 0000000..36d098a --- /dev/null +++ b/src/MAX3133X/MAX3133X_registers.h @@ -0,0 +1,654 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef MAX3133X_REGS_HPP_ +#define MAX3133X_REGS_HPP_ + +#define MAX3133X_I2C_ADDRESS 0x68 +#define REG_NOT_AVAILABLE 0xFF + +/** + * @brief STATUS Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char a1f : 1; + unsigned char a2f : 1; + unsigned char tif : 1; + unsigned char dif : 1; + unsigned char vbatlow : 1; + unsigned char pfail : 1; + unsigned char osf : 1; + unsigned char psdect : 1; + } bits; +} max3133x_status_reg_t; + +/** + * @brief ENT_EN Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char a1ie : 1; + unsigned char a2ie : 1; + unsigned char tie : 1; + unsigned char die : 1; + unsigned char vbatlowie : 1; + unsigned char pfaile : 1; + unsigned char dosf : 1; + unsigned char : 1; + } bits; +} max3133x_int_en_reg_t; + +/** + * @brief RTC_RESET Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char swrst : 1; + unsigned char : 7; + } bits; +} max3133x_rtc_reset_reg_t; + +/** + * @brief RTC_CONFIG1 Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char en_osc : 1; + unsigned char i2c_timeout : 1; + unsigned char data_ret : 1; + unsigned char dip : 1; + unsigned char a1ac : 2; + unsigned char : 2; + } bits; +} max3133x_rtc_config1_reg_t; + +/** + * @brief RTC_CONFIG2 Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char clko_hz : 2; + unsigned char enclko : 1; + unsigned char : 5; + } bits; +} max31331_rtc_config2_reg_t; + +/** + * @brief RTC_CONFIG2 Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char clko_hz : 2; + unsigned char enclko : 1; + unsigned char ddb : 1; + unsigned char dse : 1; + unsigned char : 2; + unsigned char slst : 1; + } bits; +} max31334_rtc_config2_reg_t; + +/** + * @brief TIMESTAMP_CONFIG Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char tse : 1; + unsigned char tsr : 1; + unsigned char tsow : 1; + unsigned char tsdin : 1; + unsigned char tspwm : 1; + unsigned char tsvlow : 1; + unsigned char : 2; + } bits; +} max3133x_timestamp_config_reg_t; + +/** + * @brief TIMER_CONFIG Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char tfs : 2; + unsigned char trpt : 1; + unsigned char tpause : 1; + unsigned char te : 1; + unsigned char : 3; + + } bits; +} max3133x_timer_config_reg_t; + +/** + * @brief SLEEP_CONFIG Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char a1we : 1; + unsigned char a2we : 1; + unsigned char twe : 1; + unsigned char dwe : 1; + unsigned char wsto : 3; + unsigned char slp : 1; + } bits; +} max31334_sleep_config_reg_t; + +/** + * @brief SECONDS_1_128 Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char _1_128s : 1; + unsigned char _1_64s : 1; + unsigned char _1_32s : 1; + unsigned char _1_16s : 1; + unsigned char _1_8s : 1; + unsigned char _1_4s : 1; + unsigned char _1_2s : 1; + unsigned char : 1; + } bits; +} max3133x_seconds_1_128_reg_t; + +/** + * @brief SECONDS Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char seconds : 4; + unsigned char sec_10 : 3; + unsigned char : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; +} max3133x_seconds_reg_t; + +/** + * @brief MINUTES Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char minutes : 4; + unsigned char min_10 : 3; + unsigned char : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; +} max3133x_minutes_reg_t; + +/** + * @brief HOURS Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char hour : 4; + unsigned char hr_10 : 1; + unsigned char am_pm : 1; + unsigned char f_24_12 : 1; + unsigned char : 1; + } bits_12hr; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd_12hr; + struct { + unsigned char hour : 4; + unsigned char hr_10 : 2; + unsigned char f_24_12 : 1; + unsigned char : 1; + } bits_24hr; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd_24hr; +} max3133x_hours_reg_t; + +/** + * @brief DAY Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char day : 3; + unsigned char : 5; + } bits; + struct { + unsigned char value : 3; + unsigned char : 5; + } bcd; +} max3133x_day_reg_t; + +/** + * @brief DATE Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char date : 4; + unsigned char date_10 : 2; + unsigned char : 2; + } bits; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd; +} max3133x_date_reg_t; + +/** + * @brief MONTH Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char month : 4; + unsigned char month_10 : 1; + unsigned char : 2; + unsigned char century : 1; + } bits; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd; +} max3133x_month_reg_t; + +/** + * @brief YEAR Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char year : 4; + unsigned char year_10 : 4; + } bits; + struct { + unsigned char value : 8; + } bcd; +} max3133x_year_reg_t; + +/** + * @brief ALM_SEC Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char seconds : 4; + unsigned char sec_10 : 3; + unsigned char am1 : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; +} max3133x_alm_sec_reg_t; + +/** + * @brief ALM_MIN Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char minutes : 4; + unsigned char min_10 : 3; + unsigned char am2 : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; +} max3133x_alm_min_reg_t; + +/** + * @brief ALM_HRS Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char hour : 4; + unsigned char hr_10 : 1; + unsigned char am_pm : 1; + unsigned char : 1; + unsigned char am3 : 1; + } bits_12hr; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd_12hr; + struct { + unsigned char hour : 4; + unsigned char hr_10 : 2; + unsigned char : 1; + unsigned char am3 : 1; + } bits_24hr; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd_24hr; +} max3133x_alm_hrs_reg_t; + +/** + * @brief ALM_DAY_DATE Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char day_date : 4; + unsigned char date_10 : 2; + unsigned char dy_dt_match : 1; + unsigned char am4 : 1; + } bits; + struct { + unsigned char value : 3; + unsigned char : 5; + } bcd_day; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd_date; +} max3133x_alm_day_date_reg_t; + +/** + * @brief ALM_MON Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char month : 4; + unsigned char month_10 : 1; + unsigned char : 1; + unsigned char am6 : 1; + unsigned char am5 : 1; + } bits; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd; +} max3133x_alm_mon_reg_t; + +/** + * @brief ALM_YEAR Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char year : 4; + unsigned char year_10 : 4; + } bits; + struct { + unsigned char value : 8; + } bcd; +} max3133x_alm_year_reg_t; + +/** + * @brief PWR_MGMT Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char manual_sel : 1; + unsigned char vback_sel : 1; + unsigned char : 1; + unsigned char en_vbat_detect : 1; + unsigned char : 4; + } bits; +} max3133x_pwr_mgmt_reg_t; + +/** + * @brief TRICKLE_REG Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char en_trickle : 1; + unsigned char trickle : 3; + unsigned char : 4; + } bits; +} max3133x_trickle_reg_reg_t; + +/** + * @brief OFFSET_HIGH Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char compword; + } bits; +} max3133x_offset_high_reg_t; + +/** + * @brief OFFSET_LOW Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char compword; + } bits; +} max3133x_offset_low_reg_t; + +/** + * @brief TS_FLAGS Register + */ +typedef union { + unsigned char raw; + struct { + unsigned char dinf : 1; + unsigned char vccf : 1; + unsigned char vbatf : 1; + unsigned char vlowf : 1; + unsigned char : 4; + } bits; +} max3133x_ts_flags_reg_t; + +typedef struct { + max3133x_seconds_1_128_reg_t seconds_1_128_reg; + max3133x_seconds_reg_t seconds_reg; + max3133x_minutes_reg_t minutes_reg; + max3133x_hours_reg_t hours_reg; + max3133x_day_reg_t day_reg; + max3133x_date_reg_t date_reg; + max3133x_month_reg_t month_reg; + max3133x_year_reg_t year_reg; +} max3133x_rtc_time_regs_t; + +typedef struct{ + max3133x_alm_sec_reg_t sec; + max3133x_alm_min_reg_t min; + max3133x_alm_hrs_reg_t hrs; + max3133x_alm_day_date_reg_t day_date; + max3133x_alm_mon_reg_t mon; + max3133x_alm_year_reg_t year; +}max3133x_alarm_regs_t; + +typedef struct { + max3133x_seconds_1_128_reg_t ts_sec_1_128_reg; + max3133x_seconds_reg_t ts_sec_reg; + max3133x_minutes_reg_t ts_min_reg; + max3133x_hours_reg_t ts_hour_reg; + max3133x_date_reg_t ts_date_reg; + max3133x_month_reg_t ts_month_reg; + max3133x_year_reg_t ts_year_reg; + max3133x_ts_flags_reg_t ts_flags_reg; +}max3133x_ts_regs_t; + +enum max31331_register_address{ + /*RTC REG*/ + MAX31331_STATUS = 0x00, + MAX31331_INT_EN = 0x01, + MAX31331_RTC_RESET = 0x02, + MAX31331_RTC_CONFIG1 = 0x03, + MAX31331_RTC_CONFIG2 = 0x04, + MAX31331_TIMESTAMP_CONFIG = 0x05, + MAX31331_TIMER_CONFIG = 0x06, + MAX31331_SECONDS_1_128 = 0x07, + MAX31331_SECONDS = 0x08, + MAX31331_MINUTES = 0x09, + MAX31331_HOURS = 0x0A, + MAX31331_DAY = 0x0B, + MAX31331_DATE = 0x0C, + MAX31331_MONTH = 0x0D, + MAX31331_YEAR = 0x0E, + MAX31331_ALM1_SEC = 0x0F, + MAX31331_ALM1_MIN = 0x10, + MAX31331_ALM1_HRS = 0x11, + MAX31331_ALM1_DAY_DATE = 0x12, + MAX31331_ALM1_MON = 0x13, + MAX31331_ALM1_YEAR = 0x14, + MAX31331_ALM2_MIN = 0x15, + MAX31331_ALM2_HRS = 0x16, + MAX31331_ALM2_DAY_DATE = 0x17, + MAX31331_TIMER_COUNT = 0x18, + MAX31331_TIMER_INIT = 0x19, + MAX31331_PWR_MGMT = 0x1A, + MAX31331_TRICKLE_REG = 0x1B, + MAX31331_OFFSET_HIGH = 0x1D, + MAX31331_OFFSET_LOW = 0x1E, + /*TS_RAM_REG*/ + MAX31331_TS0_SEC_1_128 = 0x20, + MAX31331_TS0_SEC = 0x21, + MAX31331_TS0_MIN = 0x22, + MAX31331_TS0_HOUR = 0x23, + MAX31331_TS0_DATE = 0x24, + MAX31331_TS0_MONTH = 0x25, + MAX31331_TS0_YEAR = 0x26, + MAX31331_TS0_FLAGS = 0x27, + MAX31331_TS1_SEC_1_128 = 0x28, + MAX31331_TS1_SEC = 0x29, + MAX31331_TS1_MIN = 0x2A, + MAX31331_TS1_HOUR = 0x2B, + MAX31331_TS1_DATE = 0x2C, + MAX31331_TS1_MONTH = 0x2D, + MAX31331_TS1_YEAR = 0x2E, + MAX31331_TS1_FLAGS = 0x2F, + MAX31331_TS2_SEC_1_128 = 0x30, + MAX31331_TS2_SEC = 0x31, + MAX31331_TS2_MIN = 0x32, + MAX31331_TS2_HOUR = 0x33, + MAX31331_TS2_DATE = 0x34, + MAX31331_TS2_MONTH = 0x35, + MAX31331_TS2_YEAR = 0x36, + MAX31331_TS2_FLAGS = 0x37, + MAX31331_TS3_SEC_1_128 = 0x38, + MAX31331_TS3_SEC = 0x39, + MAX31331_TS3_MIN = 0x3A, + MAX31331_TS3_HOUR = 0x3B, + MAX31331_TS3_DATE = 0x3C, + MAX31331_TS3_MONTH = 0x3D, + MAX31331_TS3_YEAR = 0x3E, + MAX31331_TS3_FLAGS = 0x3F, + MAX31331_END +}; + +enum max31334_register_address{ + /*RTC REG*/ + MAX31334_STATUS = 0x00, + MAX31334_INT_EN = 0x01, + MAX31334_RTC_RESET = 0x02, + MAX31334_RTC_CONFIG1 = 0x03, + MAX31334_RTC_CONFIG2 = 0x04, + MAX31334_TIMESTAMP_CONFIG = 0x05, + MAX31334_TIMER_CONFIG = 0x06, + MAX31334_SLEEP_CONFIG = 0x07, + MAX31334_SECONDS_1_128 = 0x08, + MAX31334_SECONDS = 0x09, + MAX31334_MINUTES = 0x0A, + MAX31334_HOURS = 0x0B, + MAX31334_DAY = 0x0C, + MAX31334_DATE = 0x0D, + MAX31334_MONTH = 0x0E, + MAX31334_YEAR = 0x0F, + MAX31334_ALM1_SEC = 0x10, + MAX31334_ALM1_MIN = 0x11, + MAX31334_ALM1_HRS = 0x12, + MAX31334_ALM1_DAY_DATE = 0x13, + MAX31334_ALM1_MON = 0x14, + MAX31334_ALM1_YEAR = 0x15, + MAX31334_ALM2_MIN = 0x16, + MAX31334_ALM2_HRS = 0x17, + MAX31334_ALM2_DAY_DATE = 0x18, + MAX31334_TIMER_COUNT2 = 0x19, + MAX31334_TIMER_COUNT1 = 0x1A, + MAX31334_TIMER_INIT2 = 0x1B, + MAX31334_TIMER_INIT1 = 0x1C, + MAX31334_PWR_MGMT = 0x1D, + MAX31334_TRICKLE_REG = 0x1E, + MAX31334_OFFSET_HIGH = 0x20, + MAX31334_OFFSET_LOW = 0x21, + /*TS_RAM_REG*/ + MAX31334_TS0_SEC_1_128 = 0x30, + MAX31334_TS0_SEC = 0x31, + MAX31334_TS0_MIN = 0x32, + MAX31334_TS0_HOUR = 0x33, + MAX31334_TS0_DATE = 0x34, + MAX31334_TS0_MONTH = 0x35, + MAX31334_TS0_YEAR = 0x36, + MAX31334_TS0_FLAGS = 0x37, + MAX31334_TS1_SEC_1_128 = 0x38, + MAX31334_TS1_SEC = 0x39, + MAX31334_TS1_MIN = 0x3A, + MAX31334_TS1_HOUR = 0x3B, + MAX31334_TS1_DATE = 0x3C, + MAX31334_TS1_MONTH = 0x3D, + MAX31334_TS1_YEAR = 0x3E, + MAX31334_TS1_FLAGS = 0x3F, + MAX31334_TS2_SEC_1_128 = 0x40, + MAX31334_TS2_SEC = 0x41, + MAX31334_TS2_MIN = 0x42, + MAX31334_TS2_HOUR = 0x43, + MAX31334_TS2_DATE = 0x44, + MAX31334_TS2_MONTH = 0x45, + MAX31334_TS2_YEAR = 0x46, + MAX31334_TS2_FLAGS = 0x47, + MAX31334_TS3_SEC_1_128 = 0x48, + MAX31334_TS3_SEC = 0x49, + MAX31334_TS3_MIN = 0x4A, + MAX31334_TS3_HOUR = 0x4B, + MAX31334_TS3_DATE = 0x4C, + MAX31334_TS3_MONTH = 0x4D, + MAX31334_TS3_YEAR = 0x4E, + MAX31334_TS3_FLAGS = 0x4F, + MAX31334_END +}; + +#endif /* MAX3133X_REGS_HPP_ */ diff --git a/src/MAX31341/MAX31341.cpp b/src/MAX31341/MAX31341.cpp new file mode 100644 index 0000000..7d74afc --- /dev/null +++ b/src/MAX31341/MAX31341.cpp @@ -0,0 +1,1010 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#include + + +#define GET_BIT_VAL(val, pos, mask) ( ( (val) & mask) >> pos ) +#define SET_BIT_VAL(val, pos, mask) ( ( ((int)val) << pos) & mask ) + +#define BCD2BIN(val) (((val) & 15) + ((val) >> 4) * 10) +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) + + +#define ALL_IRQ ( MAX31341_F_INT_EN_A1IE | \ + MAX31341_F_INT_EN_A2IE | \ + MAX31341_F_INT_EN_TIE | \ + MAX31341_F_INT_EN_EIE1 | \ + MAX31341_F_INT_EN_ANA_IE | \ + MAX31341_F_INT_EN_DOSF ) + +int MAX31341::read_register(uint8_t reg, uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + int counter = 0; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + + /* + stop = true, sends a stop message after transmission, releasing the I2C bus. + + stop = false, sends a restart message after transmission. + The bus will not be released, which prevents another master device from transmitting between messages. + This allows one master device to send multiple transmissions while in control. + */ + ret = m_i2c->endTransmission(false); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + if (ret != 0) { + m_i2c->begin(); // restart + return -1; + } + + // Read + m_i2c->requestFrom((char)m_slave_addr, (char)len, true); + + while (m_i2c->available()) { // slave may send less than requested + buf[counter++] = m_i2c->read(); // receive a byte as character + } + + // + if (counter != len) { + m_i2c->begin(); // restart + ret = -1; + } + + return ret; +} + +int MAX31341::write_register(uint8_t reg, const uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + m_i2c->write(buf, len); + ret = m_i2c->endTransmission(); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + + if (ret != 0) { + m_i2c->begin(); // restart + } + + return ret; +} + +/*****************************************************************************/ +MAX31341::MAX31341(TwoWire *i2c, uint8_t i2c_addr) +{ + if (i2c == NULL) { + while (1) { + ; + } + } + m_i2c = i2c; + m_slave_addr = i2c_addr; +} + +void MAX31341::begin(void) +{ + m_i2c->begin(); + + sw_reset_release(); + rtc_start(); + irq_disable(); +} + +int MAX31341::get_version(uint8_t &version) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_REV_ID, &val8); + version = GET_BIT_VAL(val8, MAX31341_F_REV_ID_REVID_POS, MAX31341_F_REV_ID_REVID); + + return ret; +} + +int MAX31341::get_status(reg_status_t &stat) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_INT_STATUS, &val8); + if (ret) { + return ret; + } + + stat.bits.a1f = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_A1IF_POS, MAX31341_F_INT_STATUS_A1IF); + stat.bits.a2f = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_A2IF_POS, MAX31341_F_INT_STATUS_A2IF); + stat.bits.tif = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_TIF_POS, MAX31341_F_INT_STATUS_TIF); + stat.bits.eif1 = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_EIF1_POS, MAX31341_F_INT_STATUS_EIF1); + stat.bits.ana_if = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_ANA_IF_POS, MAX31341_F_INT_STATUS_ANA_IF); + stat.bits.osf = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_OSF_POS, MAX31341_F_INT_STATUS_OSF); + stat.bits.los = GET_BIT_VAL(val8, MAX31341_F_INT_STATUS_LOS_POS, MAX31341_F_INT_STATUS_LOS); + + return ret; +} + +int MAX31341::get_configuration(reg_cfg_t &cfg) +{ + int ret; + uint8_t regs[2]; + + ret = read_register(MAX31341_R_CFG1, regs, 2); + if (ret) { + return ret; + } + + // configuration byte 1 + cfg.bits.swrstn = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_SWRSTN_POS, MAX31341_F_CFG1_SWRSTN); + cfg.bits.rs = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_RS_POS, MAX31341_F_CFG1_RS); + cfg.bits.osconz = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_OSCONZ_POS, MAX31341_F_CFG1_OSCONZ); + cfg.bits.clksel = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_CLKSEL_POS, MAX31341_F_CFG1_CLKSEL); + cfg.bits.intcn = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_INTCN_POS, MAX31341_F_CFG1_INTCN); + cfg.bits.eclk = GET_BIT_VAL(regs[0], MAX31341_F_CFG1_ECLK_POS, MAX31341_F_CFG1_ECLK); + + // configuration byte 2 + cfg.bits.set_rtc = GET_BIT_VAL(regs[1], MAX31341_F_CFG2_SET_RTC_POS, MAX31341_F_CFG2_SET_RTC); + cfg.bits.rd_rtc = GET_BIT_VAL(regs[1], MAX31341_F_CFG2_RD_RTC_POS, MAX31341_F_CFG2_RD_RTC); + cfg.bits.i2c_timeout = GET_BIT_VAL(regs[1], MAX31341_F_CFG2_I2C_TIMEOUT_POS, MAX31341_F_CFG2_I2C_TIMEOUT); + cfg.bits.bref = GET_BIT_VAL(regs[1], MAX31341_F_CFG2_BREF_POS, MAX31341_F_CFG2_BREF); + cfg.bits.data_reten = GET_BIT_VAL(regs[1], MAX31341_F_CFG2_DATA_RETEN_POS, MAX31341_F_CFG2_DATA_RETEN); + + return ret; +} + +int MAX31341::set_configuration(reg_cfg_t cfg) +{ + int ret; + uint8_t regs[2] = {0, }; + + // configuration byte 1 + regs[0] |= SET_BIT_VAL(cfg.bits.swrstn, MAX31341_F_CFG1_SWRSTN_POS, MAX31341_F_CFG1_SWRSTN); + regs[0] |= SET_BIT_VAL(cfg.bits.rs, MAX31341_F_CFG1_RS_POS, MAX31341_F_CFG1_RS); + regs[0] |= SET_BIT_VAL(cfg.bits.osconz, MAX31341_F_CFG1_OSCONZ_POS, MAX31341_F_CFG1_OSCONZ); + regs[0] |= SET_BIT_VAL(cfg.bits.clksel, MAX31341_F_CFG1_CLKSEL_POS, MAX31341_F_CFG1_CLKSEL); + regs[0] |= SET_BIT_VAL(cfg.bits.intcn, MAX31341_F_CFG1_INTCN_POS, MAX31341_F_CFG1_INTCN); + regs[0] |= SET_BIT_VAL(cfg.bits.eclk, MAX31341_F_CFG1_ECLK_POS, MAX31341_F_CFG1_ECLK); + + // configuration byte 2 + regs[1] |= SET_BIT_VAL(cfg.bits.set_rtc, MAX31341_F_CFG2_SET_RTC_POS, MAX31341_F_CFG2_SET_RTC); + regs[1] |= SET_BIT_VAL(cfg.bits.rd_rtc, MAX31341_F_CFG2_RD_RTC_POS, MAX31341_F_CFG2_RD_RTC); + regs[1] |= SET_BIT_VAL(cfg.bits.i2c_timeout,MAX31341_F_CFG2_I2C_TIMEOUT_POS, MAX31341_F_CFG2_I2C_TIMEOUT); + regs[1] |= SET_BIT_VAL(cfg.bits.bref, MAX31341_F_CFG2_BREF_POS, MAX31341_F_CFG2_BREF); + regs[1] |= SET_BIT_VAL(cfg.bits.data_reten, MAX31341_F_CFG2_DATA_RETEN_POS, MAX31341_F_CFG2_DATA_RETEN); + + ret = write_register(MAX31341_R_CFG1, regs, 2); + + return ret; +} + +int MAX31341::get_time(struct tm *time) +{ + int ret; + regs_rtc_time_t regs; + + if (time == NULL) { + return -1; + } + + ret = read_register(MAX31341_R_SECONDS, (uint8_t *) ®s, sizeof(regs)); + if (ret) { + return ret; + } + + /* tm_sec seconds [0,61] */ + time->tm_sec = BCD2BIN(regs.seconds.bcd.value); + /* tm_min minutes [0,59] */ + time->tm_min = BCD2BIN(regs.minutes.bcd.value); + /* tm_hour hour [0,23] */ + time->tm_hour = BCD2BIN(regs.hours.bcd.value); + /* tm_wday day of week [0,6] (Sunday = 0) */ + time->tm_wday = BCD2BIN(regs.day.bcd.value) - 1; + /* tm_mday day of month [1,31] */ + time->tm_mday = BCD2BIN(regs.date.bcd.value); + /* tm_mon month of year [0,11] */ + time->tm_mon = BCD2BIN(regs.month.bcd.value) - 1; + + /* tm_year years since 2000 */ + if (regs.month.bits.century) { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 200; + } else { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 100; + } + + /* tm_yday day of year [0,365] */ + time->tm_yday = 0; /* TODO */ + /* tm_isdst daylight savings flag */ + time->tm_isdst = 0; /* TODO */ + + return ret; +} + +int MAX31341::set_time(const struct tm *time) +{ + int ret; + regs_rtc_time_t regs; + + if (time == NULL) { + return -1; + } + + /********************************************************* + * +----------+------+---------------------------+-------+ + * | Member | Type | Meaning | Range | + * +----------+------+---------------------------+-------+ + * | tm_sec | int | seconds after the minute | 0-61* | + * | tm_min | int | minutes after the hour | 0-59 | + * | tm_hour | int | hours since midnight | 0-23 | + * | tm_mday | int | day of the month | 1-31 | + * | tm_mon | int | months since January | 0-11 | + * | tm_year | int | years since 1900 | | + * | tm_wday | int | days since Sunday | 0-6 | + * | tm_yday | int | days since January 1 | 0-365 | + * | tm_isdst | int | Daylight Saving Time flag | | + * +----------+------+---------------------------+-------+ + * * tm_sec is generally 0-59. The extra range is to accommodate for leap + * seconds in certain systems. + *********************************************************/ + regs.seconds.bcd.value = BIN2BCD(time->tm_sec); + regs.minutes.bcd.value = BIN2BCD(time->tm_min); + regs.hours.bcd.value= BIN2BCD(time->tm_hour); + regs.day.bcd.value = BIN2BCD(time->tm_wday + 1); + regs.date.bcd.value = BIN2BCD(time->tm_mday); + regs.month.bcd.value = BIN2BCD(time->tm_mon + 1); + + if (time->tm_year >= 200) { + regs.month.bits.century = 1; + regs.year.bcd.value = BIN2BCD(time->tm_year - 200); + } else if (time->tm_year >= 100) { + regs.month.bits.century = 0; + regs.year.bcd.value = BIN2BCD(time->tm_year - 100); + } else { + //Invalid set date! + return -1; + } + + ret = write_register(MAX31341_R_SECONDS, (uint8_t *)®s, sizeof(regs)); + if (ret) { + return ret; + } + + + /* + * Set RTC + */ + uint8_t val8; + + /* Toggle Set_RTC bit to set RTC registers */ + ret = read_register(MAX31341_R_CFG2, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_CFG2_SET_RTC; + ret = write_register(MAX31341_R_CFG2, &val8); + if (ret) { + return ret; + } + + delay(10); + + val8 |= MAX31341_F_CFG2_SET_RTC; + ret = write_register(MAX31341_R_CFG2, &val8); + if (ret) { + return ret; + } + + /* SET_RTC bit should be kept high at least 10ms */ + delay(10); + + val8 &= ~MAX31341_F_CFG2_SET_RTC; + ret = write_register(MAX31341_R_CFG2, &val8); + + + return ret; +} + +int MAX31341::set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period) +{ + int ret; + regs_alarm_t regs; + + if (alarm_no == ALARM2) { + switch (period) { + case ALARM_PERIOD_EVERYSECOND: + return -1; // not support for alarm 2 + } + } + + /* + * Set period + */ + regs.sec.bits.axm1 = 1; + regs.min.bits.axm2 = 1; + regs.hrs.bits.axm3 = 1; + regs.day_date.bits.axm4 = 1; + regs.day_date.bits.dy_dt = 1; + + switch (period) { + case ALARM_PERIOD_EVERYSECOND: + // Do nothing + break; + case ALARM_PERIOD_EVERYMINUTE: + regs.sec.bits.axm1 = 0; + break; + case ALARM_PERIOD_HOURLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + break; + case ALARM_PERIOD_DAILY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + break; + case ALARM_PERIOD_WEEKLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + regs.day_date.bits.axm4 = 0; + break; + case ALARM_PERIOD_MONTHLY: + regs.sec.bits.axm1 = 0; + regs.min.bits.axm2 = 0; + regs.hrs.bits.axm3 = 0; + regs.day_date.bits.axm4 = 0; + regs.day_date.bits.dy_dt = 0; + break; + default: + return -1; + } + + /* + * Convert time structure to alarm registers + */ + regs.sec.bcd.value = BIN2BCD(alarm_time->tm_sec); + regs.min.bcd.value = BIN2BCD(alarm_time->tm_min); + regs.hrs.bcd.value = BIN2BCD(alarm_time->tm_hour); + + if (regs.day_date.bits.dy_dt == 0) { + /* Date match */ + regs.day_date.bcd_date.value = BIN2BCD(alarm_time->tm_mday); + } else { + /* Day match */ + regs.day_date.bcd_day.value = BIN2BCD(alarm_time->tm_wday); + } + //regs.mon.bcd.value = BIN2BCD(alarm_time->tm_mon); + if (ret) { + return ret; + } + + /* + * Write Registers + */ + uint8_t *ptr_regs = (uint8_t *)®s; + + if (alarm_no == ALARM1) { + ret = write_register(MAX31341_R_ALM1_SEC, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + /* Discard sec starts from min register */ + ret = write_register(MAX31341_R_ALM2_MIN, &ptr_regs[1], sizeof(regs_alarm_t)-1); + } + + return ret; +} + +int MAX31341::get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled) +{ + int ret; + regs_alarm_t regs; + uint8_t *ptr_regs = (uint8_t *)®s; + + if (alarm_no == ALARM1) { + ret = read_register(MAX31341_R_ALM1_SEC, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + regs.sec.raw = 0; /* zeroise second register for alarm2 */ + /* starts from min register (no sec register) */ + ret = read_register(MAX31341_R_ALM2_MIN, &ptr_regs[1], sizeof(regs_alarm_t)-1); + } + if (ret) { + return ret; + } + + /* + * Convert alarm registers to time structure + */ + alarm_time->tm_sec = BCD2BIN(regs.sec.bcd.value); + alarm_time->tm_min = BCD2BIN(regs.min.bcd.value); + alarm_time->tm_hour = BCD2BIN(regs.hrs.bcd.value); + + if (regs.day_date.bits.dy_dt == 0) { /* date */ + alarm_time->tm_mday = BCD2BIN(regs.day_date.bcd_date.value); + } else { /* day */ + alarm_time->tm_wday = BCD2BIN(regs.day_date.bcd_day.value); + } + //alarm_time->tm_mon = BCD2BIN(regs.mon.bcd.value) - 1; + //alarm_time->tm_year = BCD2BIN(regs.year.bcd.value) + 100; /* XXX no century bit */ + + + /* + * Find period + */ + *period = (alarm_no == ALARM1) ? ALARM_PERIOD_EVERYSECOND : ALARM_PERIOD_EVERYMINUTE; + + if ((alarm_no == ALARM1) && (regs.sec.bits.axm1 == 0)) *period = ALARM_PERIOD_EVERYMINUTE; + if (regs.min.bits.axm2 == 0) *period = ALARM_PERIOD_HOURLY; + if (regs.hrs.bits.axm3 == 0) *period = ALARM_PERIOD_DAILY; + if (regs.day_date.bits.axm4 == 0) *period = ALARM_PERIOD_WEEKLY; + if (regs.day_date.bits.dy_dt == 0) *period = ALARM_PERIOD_MONTHLY; + //if ((alarm_no == ALARM1) && (regs.mon.bits.axm5 == 0)) *period = ALARM_PERIOD_YEARLY; + //if ((alarm_no == ALARM1) && (regs.mon.bits.axm6 == 0)) *period = ALARM_PERIOD_ONETIME; + + + /* + * Get enable status + */ + uint8_t val8; + ret = read_register(MAX31341_R_INT_EN, &val8); + if (ret) { + return ret; + } + + if (alarm_no == ALARM1) { + *is_enabled = (val8 & (1 << INTR_ID_ALARM1)) != 0; + } else { + *is_enabled = (val8 & (1 << INTR_ID_ALARM2)) != 0; + } + + return ret; +} + +int MAX31341::set_power_mgmt_mode(power_mgmt_mode_t mode) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_PWR_MGMT, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_PWR_MGMT_D_MODE; + val8 |= SET_BIT_VAL(mode, MAX31341_F_PWR_MGMT_D_MODE_POS, MAX31341_F_PWR_MGMT_D_MODE); + + ret = write_register(MAX31341_R_PWR_MGMT, &val8); + + return ret; +} + +int MAX31341::comparator_threshold_level(comp_thresh_t th) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG2, &val8); + if (ret) { + return ret; + } + + val8 &= MAX31341_F_CFG2_BREF; + val8 |= SET_BIT_VAL(th, MAX31341_F_CFG2_BREF_POS, MAX31341_F_CFG2_BREF); + + ret = write_register(MAX31341_R_CFG2, &val8); + + return ret; +} + +int MAX31341::supply_select(power_mgmt_supply_t supply) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_PWR_MGMT, &val8); + if (ret) { + return ret; + } + + switch (supply) { + case POW_MGMT_SUPPLY_SEL_VCC: + val8 |= MAX31341_F_PWR_MGMT_DMAN_SEL; + val8 &= ~MAX31341_F_PWR_MGMT_D_VBACK_SEL; + break; + case POW_MGMT_SUPPLY_SEL_AIN: + val8 |= MAX31341_F_PWR_MGMT_DMAN_SEL; + val8 |= MAX31341_F_PWR_MGMT_D_VBACK_SEL; + break; + case POW_MGMT_SUPPLY_SEL_AUTO: + default: + val8 &= ~MAX31341_F_PWR_MGMT_DMAN_SEL; + break; + } + + ret = write_register(MAX31341_R_PWR_MGMT, &val8); + + return ret; +} + +int MAX31341::trickle_charger_enable(trickle_charger_ohm_t res) +{ + int ret; + uint8_t val8 = 0; + + val8 |= SET_BIT_VAL(res, MAX31341_F_TRICKLE_D_TRICKLE_POS, MAX31341_F_TRICKLE_D_TRICKLE); + ret = write_register(MAX31341_R_TRICKLE, &val8); + + return ret; +} + +int MAX31341::trickle_charger_disable() +{ + int ret; + uint8_t val8; + + val8 = 0; + ret = write_register(MAX31341_R_TRICKLE, &val8); + + return ret; +} + +int MAX31341::set_square_wave_frequency(sqw_out_freq_t freq) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_CFG1_RS; + val8 |= SET_BIT_VAL(freq, MAX31341_F_CFG1_RS_POS, MAX31341_F_CFG1_RS); + + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} + +int MAX31341::set_clock_sync_delay(sync_delay_t delay) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CLOCK_SYNC, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_CLOCK_SYNC_SYNC_DELAY; + val8 |= SET_BIT_VAL(delay, MAX31341_F_CLOCK_SYNC_SYNC_DELAY_POS, MAX31341_F_CLOCK_SYNC_SYNC_DELAY); + + ret = write_register(MAX31341_R_CLOCK_SYNC, &val8); + + return ret; +} + +int MAX31341::set_clkin_frequency(clkin_freq_t freq) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_CFG1_CLKSEL; + val8 |= SET_BIT_VAL(freq, MAX31341_F_CFG1_CLKSEL_POS, MAX31341_F_CFG1_CLKSEL); + + ret = write_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + if (freq == CLKIN_FREQ_1HZ) { + ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_1SEC); + } else { + ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_100MS); + } + + return ret; +} + +int MAX31341::configure_intb_clkout_pin(config_intb_clkout_pin_t sel) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + if (sel == CONFIGURE_PIN_AS_INTB) { + val8 |= MAX31341_F_CFG1_INTCN; + } else { + val8 &= ~MAX31341_F_CFG1_INTCN; + } + + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} + +int MAX31341::configure_inta_clkin_pin(config_inta_clkin_pin_t sel) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + if (sel == CONFIGURE_PIN_AS_CLKIN) { + val8 |= MAX31341_F_CFG1_ECLK; + } else { + val8 &= ~MAX31341_F_CFG1_ECLK; + } + + ret = write_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + if (sel == CONFIGURE_PIN_AS_CLKIN) { + /* Default synchronization delay for external clock mode */ + ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_1SEC); + } else { + /* Synchronization delay for internal oscillator mode */ + ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_10MS); + } + + return ret; +} + +int MAX31341::timer_init(uint8_t initial_value, bool repeat, timer_freq_t freq) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_TIMER_CFG_TE; + val8 |= MAX31341_F_TIMER_CFG_TPAUSE; + if (repeat) { + val8 |= MAX31341_F_TIMER_CFG_TRPT; + } else { + val8 &= ~MAX31341_F_TIMER_CFG_TRPT; + } + val8 &= ~MAX31341_F_TIMER_CFG_TFS; + val8 |= SET_BIT_VAL(freq, MAX31341_F_TIMER_CFG_TFS_POS, MAX31341_F_TIMER_CFG_TFS); + + ret = write_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + ret = write_register(MAX31341_R_TIMER_INIT, &initial_value); + + return ret; +} + +int MAX31341::timer_get(uint8_t &count) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_COUNT, &val8); + if (ret) { + return ret; + } + count = val8; + + return ret; +} + +int MAX31341::timer_start() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + val8 |= MAX31341_F_TIMER_CFG_TE; + val8 &= ~MAX31341_F_TIMER_CFG_TPAUSE; + + ret = write_register(MAX31341_R_TIMER_CFG, &val8); + + return ret; +} + +int MAX31341::timer_pause() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + val8 |= MAX31341_F_TIMER_CFG_TE; + val8 |= MAX31341_F_TIMER_CFG_TPAUSE; + + ret = write_register(MAX31341_R_TIMER_CFG, &val8); + + return ret; +} + +int MAX31341::timer_continue() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + val8 |= MAX31341_F_TIMER_CFG_TE; + val8 &= ~MAX31341_F_TIMER_CFG_TPAUSE; + + ret = write_register(MAX31341_R_TIMER_CFG, &val8); + + return ret; +} + +int MAX31341::timer_stop() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_TIMER_CFG, &val8); + if (ret) { + return ret; + } + + val8 &= ~MAX31341_F_TIMER_CFG_TE; + val8 |= MAX31341_F_TIMER_CFG_TPAUSE; + + ret = write_register(MAX31341_R_TIMER_CFG, &val8); + + return ret; +} + +int MAX31341::set_data_retention_mode(bool enable) +{ + int ret; + uint8_t regs[2]; + + ret = read_register(MAX31341_R_CFG1, regs, 2); + if (ret) { + return ret; + } + + if (enable) { + regs[0] |= MAX31341_F_CFG1_OSCONZ; + regs[1] |= MAX31341_F_CFG2_DATA_RETEN; + } else { + regs[0] &= ~MAX31341_F_CFG1_OSCONZ; + regs[1] &= ~MAX31341_F_CFG2_DATA_RETEN; + } + + ret = write_register(MAX31341_R_CFG1, regs, 2); + + return ret; +} + +int MAX31341::irq_enable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_INT_EN, &val8); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 |= ALL_IRQ; + } else { + val8 |= id; + } + + ret = write_register(MAX31341_R_INT_EN, &val8); + + return ret; +} + +int MAX31341::irq_disable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_INT_EN, &val8); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 &= ~ALL_IRQ; + } else { + val8 &= ~id; + } + + ret = write_register(MAX31341_R_INT_EN, &val8); + + return ret; +} + +int MAX31341::irq_clear_flag(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + // read status register to clear flags + ret = read_register(MAX31341_R_INT_STATUS, &val8); + + return ret; +} + +int MAX31341::sw_reset_assert() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + /* Put device in reset state */ + val8 &= ~MAX31341_F_CFG1_SWRSTN; + + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} + +int MAX31341::sw_reset_release() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + /* Remove device from reset state */ + val8 |= MAX31341_F_CFG1_SWRSTN; + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} + +int MAX31341::rtc_start() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + /* Enable the oscillator */ + val8 &= ~MAX31341_F_CFG1_OSCONZ; + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} +int MAX31341::rtc_stop() +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31341_R_CFG1, &val8); + if (ret) { + return ret; + } + + /* Disable the oscillator */ + val8 |= MAX31341_F_CFG1_OSCONZ; + ret = write_register(MAX31341_R_CFG1, &val8); + + return ret; +} + +int MAX31341::nvram_size() +{ + return (MAX31341_R_RAM_END - MAX31341_R_RAM_START) + 1; +} + +int MAX31341::nvram_write(const uint8_t *buffer, int offset, int length) +{ + int ret; + int totlen; + + totlen = (MAX31341_R_RAM_END - MAX31341_R_RAM_START) + 1; + + if ((offset + length) > totlen) { + return -1; + } + + if (length == 0) { + return 0; + } + + ret = write_register(MAX31341_R_RAM_START + offset, buffer, length); + + return ret; +} + +int MAX31341::nvram_read(uint8_t *buffer, int offset, int length) +{ + int ret; + int totlen; + + totlen = (MAX31341_R_RAM_END - MAX31341_R_RAM_START) + 1; + + if ((offset + length) > totlen) { + return -1; + } + + if (length == 0) { + return 0; + } + + ret = read_register(MAX31341_R_RAM_START + offset, buffer, length); + + return ret; +} diff --git a/src/MAX31341/MAX31341.h b/src/MAX31341/MAX31341.h new file mode 100644 index 0000000..f5067a9 --- /dev/null +++ b/src/MAX31341/MAX31341.h @@ -0,0 +1,707 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef _MAX31341_H_ +#define _MAX31341_H_ + +#include + +#include +#include +#include + + +#define MAX31341_DRV_VERSION "v1.0.1" + + +class MAX31341 +{ +public: + /** + * @brief Mode of the comparator + */ + typedef enum { + POW_MGMT_MODE_COMPARATOR, /**< Comparator */ + POW_MGMT_MODE_POWER_MANAGEMENT, /**< Power Management / Trickle Charger Mode */ + } power_mgmt_mode_t; + + /** + * @brief Analog comparator threshold voltage + */ + typedef enum { + COMP_THRESH_1V3, /**< 1.3V */ + COMP_THRESH_1V7, /**< 1.7V */ + COMP_THRESH_2V0, /**< 2.0V */ + COMP_THRESH_2V2, /**< 2.2V */ + } comp_thresh_t; + + /** + * @brief Supply voltage select. + */ + typedef enum { + POW_MGMT_SUPPLY_SEL_AUTO, /**< Circuit decides whether to use VCC or VBACKUP */ + POW_MGMT_SUPPLY_SEL_VCC, /**< Use VCC as supply */ + POW_MGMT_SUPPLY_SEL_AIN, /**< Use AIN as supply */ + } power_mgmt_supply_t; + + /** + * @brief Selection of charging path's resistor value + */ + typedef enum { + TRICKLE_CHARGER_NO_CONNECTION = 0, /**< No connect */ + TRICKLE_CHARGER_3K_S = 0x08, /**< 3Kohm in series with a Schottky diode. */ + TRICKLE_CHARGER_6K_S = 0x0A, /**< 6Kohm in series with a Schottky diode. */ + TRICKLE_CHARGER_11K_S = 0x0B, /**< 11Kohm in series with a Schottky diode. */ + TRICKLE_CHARGER_3K_S_2 = 0x0C, /**< 3Kohm in series with a diode in series with a Schottky diode. */ + TRICKLE_CHARGER_6K_S_2 = 0x0E, /**< 6Kohm in series with a diode in series with a Schottky diode. */ + TRICKLE_CHARGER_11K_S_2 = 0x0F, /**< 11Kohm in series with a diode in series with a Schottky diode. */ + } trickle_charger_ohm_t; + + /** + * @brief Timer frequency selection + */ + typedef enum { + TIMER_FREQ_1024HZ, /**< 1024Hz */ + TIMER_FREQ_256HZ, /**< 256Hz */ + TIMER_FREQ_64HZ, /**< 64Hz */ + TIMER_FREQ_16HZ, /**< 16Hz */ + } timer_freq_t; + + /** + * @brief CLKIN frequency selection + */ + typedef enum { + CLKIN_FREQ_1HZ, /**< 1Hz*/ + CLKIN_FREQ_50HZ, /**< 50Hz */ + CLKIN_FREQ_60HZ, /**< 60Hz */ + CLKIN_FREQ_32768HZ, /**< 32.768kHz */ + } clkin_freq_t; + + /** + * @brief Square wave output frequency selection on CLKOUT pin + */ + typedef enum { + SQW_OUT_FREQ_1HZ, /**< 1Hz */ + SQW_OUT_FREQ_4KHZ, /**< 4.098kHz */ + SQW_OUT_FREQ_8KHZ, /**< 8.192kHz */ + SQW_OUT_FREQ_32KHZ, /**< 32.768kHz */ + } sqw_out_freq_t; + + /** + * @brief Selection of interrupt ids + */ + typedef enum { + INTR_ID_ALARM1 = 1<<0, /**< Alarm1 flag */ + INTR_ID_ALARM2 = 1<<1, /**< Alarm2 flag */ + INTR_ID_TIMER = 1<<2, /**< Timer interrupt flag */ + INTR_ID_RESERVED = 1<<3, + INTR_ID_EXTERNAL = 1<<4, /**< External interrupt flag for DIN1 */ + INTR_ID_ANALOG = 1<<5, /**< Analog Interrupt flag / Power fail flag */ + INTR_ID_OSF = 1<<6, /**< Oscillator stop flag */ + INTR_ID_ALL = 0xFF + } intr_id_t; + + /** + * @brief Alarm number selection + */ + typedef enum { + ALARM1, /**< Alarm number 1 */ + ALARM2, /**< Alarm number 2 */ + } alarm_no_t; + + /** + * @brief Alarm periodicity selection + */ + typedef enum { + ALARM_PERIOD_EVERYSECOND, /**< Once per second */ + ALARM_PERIOD_EVERYMINUTE, /**< Second match / Once per minute */ + ALARM_PERIOD_HOURLY, /**< Second and Minute match */ + ALARM_PERIOD_DAILY, /**< Hour, Minute and Second match*/ + ALARM_PERIOD_WEEKLY, /**< Day and Time match */ + ALARM_PERIOD_MONTHLY /**< Date and Time match */ + } alarm_period_t; + + /** + * @brief Selection of INTA/CLKIN pin function + */ + typedef enum { + CONFIGURE_PIN_AS_INTA, /**< Configure pin as interrupt out */ + CONFIGURE_PIN_AS_CLKIN /**< Configure pin as external clock in */ + } config_inta_clkin_pin_t; + + /** + * @brief Selection of INTB/CLKOUT pin function + */ + typedef enum { + CONFIGURE_PIN_AS_CLKOUT, /**< Output is square wave */ + CONFIGURE_PIN_AS_INTB /**< Output is interrupt */ + } config_intb_clkout_pin_t; + + /** + * @brief Selection of sync delay + */ + typedef enum { + SYNC_DLY_LESS_THAN_1SEC = 0, /**< Sync delay less than 1 second, recommended for external 1Hz clock */ + SYNC_DLY_LESS_THAN_100MS, /**< Sync delay less than 100 msec, recommended for external 50Hz/60Hz/32KHz clock */ + SYNC_DLY_LESS_THAN_10MS /**< Sync delay less than 10 msec, recommended for internal clock */ + } sync_delay_t; + + typedef union { + uint8_t raw; + struct { + uint8_t a1f : 1; + uint8_t a2f : 1; + uint8_t tif : 1; + uint8_t : 1; + uint8_t eif1 : 1; + uint8_t ana_if : 1; + uint8_t osf : 1; + uint8_t los : 1; + } bits; + } reg_status_t; + + typedef union { + uint16_t raw; + struct { + uint16_t swrstn : 1; + uint16_t rs : 2; + uint16_t osconz : 1; + uint16_t clksel : 2; + uint16_t intcn : 1; + uint16_t eclk : 1; + uint16_t : 1; // not used + uint16_t set_rtc : 1; + uint16_t rd_rtc : 1; + uint16_t i2c_timeout : 1; + uint16_t bref : 2; + uint16_t data_reten : 1; + uint16_t : 1; // not used + } bits; + } reg_cfg_t; + + /** + * @brief Base class constructor. + * + * @param[in] i2c Pointer to I2C bus object for this device. + * @param[in] i2c_addr slave addr + */ + MAX31341(TwoWire *i2c, uint8_t i2c_addr); + + /** + * @brief First initialization, must be called before using class function + * + */ + void begin(void); + + /** + * @brief Get Revision ID of sensor + * + * @param[out] version Pointer to save version number. + * + * @returns 0 on success, negative error code on failure. + */ + int get_version(uint8_t &version); + + /** + * @brief Read status byte + * + * @param[out] stat: Decoded status byte + * + * @returns 0 on success, negative error code on failure. + */ + int get_status(reg_status_t &stat); + + /** + * @brief Get configuration bytes + * + * @param[out] cfg: configuration values + * + * @returns 0 on success, negative error code on failure. + */ + int get_configuration(reg_cfg_t &cfg); + + /** + * @brief Set configuration bytes + * + * @param[in] cfg: configuration values + * + * @returns 0 on success, negative error code on failure. + */ + int set_configuration(reg_cfg_t cfg); + + /** + * @brief Read time info from RTC. + * + * @param[out] rtc_time Time info from RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int get_time(struct tm *rtc_ctime); + + /** + * @brief Set time info to RTC. + * + * @param[in] rtc_time Time info to be written to RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int set_time(const struct tm *rtc_ctime); + + /** + * @brief Set an alarm condition + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[in] alarm_time Pointer to alarm time to be set + * @param[in] period Alarm periodicity, one of ALARM_PERIOD_* + * + * @return 0 on success, error code on failure + */ + int set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period); + + /** + * @brief Get alarm data & time + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[out] alarm_time Pointer to alarm time to be filled in + * @param[out] period Pointer to the period of alarm, one of ALARM_PERIOD_* + * @param[out] is_enabled Pointer to the state of alarm + * + * @return 0 on success, error code on failure + */ + int get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled); + + /** + * @brief Select power management mode of operation + * + * @param[in] mode Mode selection, one of COMP_MODE_* + * + * @return 0 on success, error code on failure + */ + int set_power_mgmt_mode(power_mgmt_mode_t mode); + + /** + * @brief Set comparator threshold + * + * @param[in] th Set Analog Comparator Threshold level, one of COMP_THRESH_* + * + * @return 0 on success, error code on failure + */ + int comparator_threshold_level(comp_thresh_t th); + + /** + * @brief Select device power source + * + * @param[in] supply Supply selection, one of POW_MGMT_SUPPLY_SEL_* + * + * @return 0 on success, error code on failure + */ + int supply_select(power_mgmt_supply_t supply); + + /** + * @brief Configure trickle charger charging path, also enable it + * + * @param[in] res Value of resister + * + * @return 0 on success, error code on failure + */ + int trickle_charger_enable(trickle_charger_ohm_t res); + + /** + * @brief Disable trickle charger + * + * @return 0 on success, error code on failure + */ + int trickle_charger_disable(); + + /** + * @brief Select square wave output frequency selection + * + * @param[in] freq Clock frequency, one of CLKOUT_FREQ_* + * + * @return 0 on success, error code on failure + */ + int set_square_wave_frequency(sqw_out_freq_t freq); + + /** + * @brief Select external clock input frequency + * + * @param[in] freq Clock frequency, one of CLKIN_FREQ_* + * + * @return 0 on success, error code on failure + */ + int set_clkin_frequency(clkin_freq_t freq); + + /** + * @brief Select direction of INTB/CLKOUT pin + * + * @param[in] sel Pin function, one of CONFIGURE_PIN_B3_AS_INTB or CONFIGURE_PIN_B3_AS_CLKOUT + * + * @return 0 on success, error code on failure + */ + int configure_intb_clkout_pin(config_intb_clkout_pin_t sel); + + /** + * @brief Select direction of INTA/CLKIN pin + * + * @param[in] sel Pin function, one of CONFIGURE_PIN_B3_AS_INTA or CONFIGURE_PIN_B3_AS_CLKIN + * + * @return 0 on success, error code on failure + */ + int configure_inta_clkin_pin(config_inta_clkin_pin_t sel); + + /** + * @brief Initialize timer + * + * @param[in] value Timer initial value + * @param[in] repeat Timer repeat mode enable/disable + * @param[in] freq Timer frequency, one of TIMER_FREQ_* + * @param[in] mode Timer mode, 0 or 1 + * + * @return 0 on success, error code on failure + * + * @note \p mode controls the countdown timer interrupt function + * along with \p repeat. + * Pulse interrupt when \p mode = 0, irrespective of \p repeat (true or false) + * Pulse interrupt when \p mode = 1 and \p repeat = true + * Level interrupt when \p mode = 1 and \p repeat = false + */ + int timer_init(uint8_t value, bool repeat, timer_freq_t freq); + + /** + * @brief Read timer value + * + * @return 0 on success, error code on failure + */ + int timer_get(uint8_t &count); + + /** + * @brief Enable timer + * + * @return 0 on success, error code on failure + */ + int timer_start(); + + /** + * @brief Pause timer, timer value is preserved + * + * @return 0 on success, error code on failure + */ + int timer_pause(); + + /** + * @brief Start timer from the paused value + * + * @return 0 on success, error code on failure + */ + int timer_continue(); + + /** + * @brief Disable timer + * + * @return 0 on success, error code on failure + */ + int timer_stop(); + + /** + * @brief Put device into data retention mode + * + * @param[in] enable: true to enter data retain mode, + * false to exit from data retain mode + * + * @return 0 on success, error code on failure + */ + int set_data_retention_mode(bool enable); + + /** + * @brief Enable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_enable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Disable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_disable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Clear the interrupt flag + * + * @return 0 on success, error code on failure + */ + int irq_clear_flag(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Put device into reset state + * + * @return 0 on success, error code on failure + */ + int sw_reset_assert(); + + /** + * @brief Release device from state state + * + * @return 0 on success, error code on failure + */ + int sw_reset_release(); + + /** + * @brief Enable the RTC oscillator + * + * @return 0 on success, error code on failure + */ + int rtc_start(); + + /** + * @brief Disable the RTC oscillator + * + * @return 0 on success, error code on failure + */ + int rtc_stop(); + + /** + * @brief NVRAM size of the part + * + * @return 0 if part does not have a NVRAM, otherwise returns size + */ + int nvram_size(); + + /** + * @brief Non-volatile memory write + * + * @param[out] buffer Pointer to the data to be written + * @param[in] offset Offset of location in NVRAM + * @param[in] length Number of bytes to write + * + * @return 0 on success, error code on failure + */ + int nvram_write(const uint8_t *buffer, int offset, int length); + + /** + * @brief Non-volatile memory read + * + * @param[in] buffer Buffer to read in to + * @param[in] offset Offset of location in NVRAM + * @param[in] length Number of bytes to read + * + * @return 0 on success, error code on failure + */ + int nvram_read(uint8_t *buffer, int offset, int length); + + /** + * @brief Read from a register. + * + * @param[in] reg Address of a register to be read. + * @param[out] value Pointer to save result value. + * @param[in] len Size of result to be read. + * + * @returns 0 on success, negative error code on failure. + */ + int read_register(uint8_t reg, uint8_t *dst, uint8_t len=1); + + /** + * @brief Write to a register. + * + * @param[in] reg Address of a register to be written. + * @param[out] value Pointer of value to be written to register. + * @param[in] len Size of result to be written. + * + * @returns 0 on success, negative error code on failure. + */ + int write_register(uint8_t reg, const uint8_t *src, uint8_t len=1); + +private: + typedef struct { + union { + uint8_t raw; + struct { + uint8_t seconds : 4; /**< RTC seconds value. */ + uint8_t sec_10 : 3; /**< RTC seconds in multiples of 10 */ + uint8_t : 1; + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } seconds; + + union { + uint8_t raw; + struct { + uint8_t minutes : 4; /**< RTC minutes value */ + uint8_t min_10 : 3; /**< RTC minutes in multiples of 10 */ + uint8_t : 1; + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } minutes; + + union { + uint8_t raw; + struct { + uint8_t hour : 4; /**< RTC hours value */ + uint8_t hr_10 : 2; /**< RTC hours in multiples of 10 */ + uint8_t : 2; + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd; + } hours; + + union { + uint8_t raw; + struct { + uint8_t day : 3; /**< RTC days */ + uint8_t : 5; + } bits; + struct { + uint8_t value : 3; + uint8_t : 5; + } bcd; + } day; + + union { + uint8_t raw; + struct { + uint8_t date : 4; /**< RTC date */ + uint8_t date_10 : 2; /**< RTC date in multiples of 10 */ + uint8_t : 2; + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd; + } date; + + union { + uint8_t raw; + struct { + uint8_t month : 4; /**< RTC months */ + uint8_t month_10 : 1; /**< RTC month in multiples of 10 */ + uint8_t : 2; + uint8_t century : 1; /**< Century bit */ + } bits; + struct { + uint8_t value : 5; + uint8_t : 3; + } bcd; + } month; + + union { + uint8_t raw; + struct { + uint8_t year : 4; /**< RTC years */ + uint8_t year_10 : 4; /**< RTC year multiples of 10 */ + } bits; + struct { + uint8_t value : 8; + } bcd; + } year; + } regs_rtc_time_t; + + + typedef struct { + union { + uint8_t raw; + struct { + uint8_t seconds : 4; /**< Alarm1 seconds */ + uint8_t sec_10 : 3; /**< Alarm1 seconds in multiples of 10 */ + uint8_t axm1 : 1; /**< Alarm1 mask bit for minutes */ + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } sec; + + union { + uint8_t raw; + struct { + uint8_t minutes : 4; /**< Alarm1 minutes */ + uint8_t min_10 : 3; /**< Alarm1 minutes in multiples of 10 */ + uint8_t axm2 : 1; /**< Alarm1 mask bit for minutes */ + } bits; + struct { + uint8_t value : 7; + uint8_t : 1; + } bcd; + } min; + + union { + uint8_t raw; + struct { + uint8_t hour : 4; /**< Alarm1 hours */ + uint8_t hr_10 : 2; /**< Alarm1 hours in multiples of 10 */ + uint8_t : 1; + uint8_t axm3 : 1; /**< Alarm1 mask bit for hours */ + } bits; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd; + } hrs; + + union { + uint8_t raw; + struct { + uint8_t day_date : 4; /**< Alarm1 day/date */ + uint8_t date_10 : 2; /**< Alarm1 date in multiples of 10 */ + uint8_t dy_dt : 1; + uint8_t axm4 : 1; /**< Alarm1 mask bit for day/date */ + } bits; + struct { + uint8_t value : 3; + uint8_t : 5; + } bcd_day; + struct { + uint8_t value : 6; + uint8_t : 2; + } bcd_date; + } day_date; + } regs_alarm_t; + + TwoWire *m_i2c; + uint8_t m_slave_addr; + + int set_clock_sync_delay(sync_delay_t delay); +}; + +#endif /* _MAX31341_H_ */ diff --git a/src/MAX31341/MAX31341_registers.h b/src/MAX31341/MAX31341_registers.h new file mode 100644 index 0000000..3a332b8 --- /dev/null +++ b/src/MAX31341/MAX31341_registers.h @@ -0,0 +1,163 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef _MAX31341_REGISTERS_H_ +#define _MAX31341_REGISTERS_H_ + + +#define MAX31341_I2C_ADDRESS 0x69 + +// Registers +#define MAX31341_R_CFG1 ( 0x00 ) +#define MAX31341_R_CFG2 ( 0x01 ) +#define MAX31341_R_INT_POLARITY_CFG ( 0x02 ) +#define MAX31341_R_TIMER_CFG ( 0x03 ) +#define MAX31341_R_INT_EN ( 0x04 ) +#define MAX31341_R_INT_STATUS ( 0x05 ) +#define MAX31341_R_SECONDS ( 0x06 ) +#define MAX31341_R_MINUTES ( 0x07 ) +#define MAX31341_R_HOURS ( 0x08 ) +#define MAX31341_R_DAY ( 0x09 ) +#define MAX31341_R_DATE ( 0x0A ) +#define MAX31341_R_MONTH ( 0x0B ) +#define MAX31341_R_YEAR ( 0x0C ) +#define MAX31341_R_ALM1_SEC ( 0x0D ) +#define MAX31341_R_ALM1_MIN ( 0x0E ) +#define MAX31341_R_ALM1_HRS ( 0x0F ) +#define MAX31341_R_ALM1DAY_DATE ( 0x10 ) +#define MAX31341_R_ALM2_MIN ( 0x11 ) +#define MAX31341_R_ALM2_HRS ( 0x12 ) +#define MAX31341_R_ALM2DAY_DATE ( 0x13 ) +#define MAX31341_R_TIMER_COUNT ( 0x14 ) +#define MAX31341_R_TIMER_INIT ( 0x15 ) +#define MAX31341_R_RAM_START ( 0x16 ) +#define MAX31341_R_RAM_END ( 0x55 ) +#define MAX31341_R_PWR_MGMT ( 0x56 ) +#define MAX31341_R_TRICKLE ( 0x57 ) +#define MAX31341_R_CLOCK_SYNC ( 0x58 ) +#define MAX31341_R_REV_ID ( 0x59 ) + + +// Config 1 register bits +#define MAX31341_F_CFG1_SWRSTN_POS 0 +#define MAX31341_F_CFG1_SWRSTN (1< +#include + + +#define GET_BIT_VAL(val, pos, mask) ( ( (val) & mask) >> pos ) +#define SET_BIT_VAL(val, pos, mask) ( ( ((int)val) << pos) & mask ) + +#define BCD2BIN(val) (((val) & 15) + ((val) >> 4) * 10) +#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10) + + +#define ALL_IRQ ( MAX31343_F_INT_EN_A1IE | \ + MAX31343_F_INT_EN_A2IE | \ + MAX31343_F_INT_EN_TIE | \ + MAX31343_F_INT_EN_TSIE | \ + MAX31343_F_INT_EN_PFAILE | \ + MAX31343_F_INT_EN_DOSF ) + + +int MAX31343::read_register(uint8_t reg, uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + int counter = 0; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + + /* + stop = true, sends a stop message after transmission, releasing the I2C bus. + + stop = false, sends a restart message after transmission. + The bus will not be released, which prevents another master device from transmitting between messages. + This allows one master device to send multiple transmissions while in control. + */ + ret = m_i2c->endTransmission(false); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + if (ret != 0) { + m_i2c->begin(); // restart + return -1; + } + + // Read + m_i2c->requestFrom((char)m_slave_addr, (char)len, true); + + while (m_i2c->available()) { // slave may send less than requested + buf[counter++] = m_i2c->read(); // receive a byte as character + } + + // + if (counter != len) { + m_i2c->begin(); // restart + ret = -1; + } + + return ret; +} + +int MAX31343::write_register(uint8_t reg, const uint8_t *buf, uint8_t len/*=1*/) +{ + int ret; + + m_i2c->beginTransmission(m_slave_addr); + m_i2c->write(reg); + m_i2c->write(buf, len); + ret = m_i2c->endTransmission(); + /* + 0:success + 1:data too long to fit in transmit buffer + 2:received NACK on transmit of address + 3:received NACK on transmit of data + 4:other error + */ + + if (ret != 0) { + m_i2c->begin(); // restart + } + + return ret; +} + +/***********************************************************************************/ +MAX31343::MAX31343(TwoWire *i2c, uint8_t i2c_addr) +{ + if (i2c == NULL) { + while (1); + } + + m_i2c = i2c; + m_slave_addr = i2c_addr; +} + +void MAX31343::begin(void) +{ + m_i2c->begin(); + + sw_reset_release(); + rtc_start(); + irq_disable(); +} + +int MAX31343::get_status(reg_status_t &stat) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31343_R_STATUS, &val8); + if (ret) { + return ret; + } + + stat.bits.a1f = GET_BIT_VAL(val8, MAX31343_F_STATUS_A1F_POS, MAX31343_F_STATUS_A1F); + stat.bits.a2f = GET_BIT_VAL(val8, MAX31343_F_STATUS_A2F_POS, MAX31343_F_STATUS_A2F); + stat.bits.tif = GET_BIT_VAL(val8, MAX31343_F_STATUS_TIF_POS, MAX31343_F_STATUS_TIF); + stat.bits.tsf = GET_BIT_VAL(val8, MAX31343_F_STATUS_TSF_POS, MAX31343_F_STATUS_TSF); + stat.bits.pfail = GET_BIT_VAL(val8, MAX31343_F_STATUS_PFAIL_POS, MAX31343_F_STATUS_PFAIL); + stat.bits.osf = GET_BIT_VAL(val8, MAX31343_F_STATUS_OSF_POS, MAX31343_F_STATUS_OSF); + stat.bits.psdect = GET_BIT_VAL(val8, MAX31343_F_STATUS_PSDECT_POS, MAX31343_F_STATUS_PSDECT); + + return ret; +} + +int MAX31343::get_configuration(reg_cfg_t &cfg) +{ + int ret; + uint8_t regs[2]; + + ret = read_register(MAX31343_R_CFG1, regs, 2); + if (ret) { + return ret; + } + + // configuration byte 1 + cfg.bits.enosc = GET_BIT_VAL(regs[0], MAX31343_F_CFG1_ENOSC_POS, MAX31343_F_CFG1_ENOSC); + cfg.bits.i2c_timeout = GET_BIT_VAL(regs[0], MAX31343_F_CFG1_I2C_TIMEOUT_POS, MAX31343_F_CFG1_I2C_TIMEOUT); + cfg.bits.data_ret = GET_BIT_VAL(regs[0], MAX31343_F_CFG1_DATA_RET_POS, MAX31343_F_CFG1_DATA_RET); + // configuration byte 2 + cfg.bits.sqw_hz = GET_BIT_VAL(regs[1], MAX31343_F_CFG2_SQW_HZ_POS, MAX31343_F_CFG2_SQW_HZ); + cfg.bits.clko_hz = GET_BIT_VAL(regs[1], MAX31343_F_CFG2_CLKO_HZ_POS, MAX31343_F_CFG2_CLKO_HZ); + cfg.bits.enclko = GET_BIT_VAL(regs[1], MAX31343_F_CFG2_ENCLKO_POS, MAX31343_F_CFG2_ENCLKO); + + return ret; +} + +int MAX31343::set_configuration(reg_cfg_t cfg) +{ + int ret; + uint8_t regs[2] = {0, }; + + regs[0] |= SET_BIT_VAL(cfg.bits.enosc, MAX31343_F_CFG1_ENOSC_POS, MAX31343_F_CFG1_ENOSC); + regs[0] |= SET_BIT_VAL(cfg.bits.i2c_timeout, MAX31343_F_CFG1_I2C_TIMEOUT_POS, MAX31343_F_CFG1_I2C_TIMEOUT); + regs[0] |= SET_BIT_VAL(cfg.bits.data_ret, MAX31343_F_CFG1_DATA_RET_POS, MAX31343_F_CFG1_DATA_RET); + // configuration byte 2 + regs[1] |= SET_BIT_VAL(cfg.bits.sqw_hz, MAX31343_F_CFG2_SQW_HZ_POS, MAX31343_F_CFG2_SQW_HZ); + regs[1] |= SET_BIT_VAL(cfg.bits.clko_hz, MAX31343_F_CFG2_CLKO_HZ_POS, MAX31343_F_CFG2_CLKO_HZ); + regs[1] |= SET_BIT_VAL(cfg.bits.enclko, MAX31343_F_CFG2_ENCLKO_POS, MAX31343_F_CFG2_ENCLKO); + + ret = write_register(MAX31343_R_CFG1, regs, 2); + + return ret; +} + +int MAX31343::get_time(struct tm *time) +{ + int ret; + rtc_time_regs_t regs; + + if (time == NULL) { + return -1; + } + + ret = read_register(MAX31343_R_SECONDS, (uint8_t *) ®s, sizeof(regs)); + if (ret) { + return ret; + } + + /* tm_sec seconds [0,61] */ + time->tm_sec = BCD2BIN(regs.seconds.bcd.value); + /* tm_min minutes [0,59] */ + time->tm_min = BCD2BIN(regs.minutes.bcd.value); + /* tm_hour hour [0,23] */ + time->tm_hour = BCD2BIN(regs.hours.bcd.value); + /* tm_wday day of week [0,6] (Sunday = 0) */ + time->tm_wday = BCD2BIN(regs.day.bcd.value) - 1; + /* tm_mday day of month [1,31] */ + time->tm_mday = BCD2BIN(regs.date.bcd.value); + /* tm_mon month of year [0,11] */ + time->tm_mon = BCD2BIN(regs.month.bcd.value) - 1; + + /* tm_year years since 2000 */ + if (regs.month.bits.century) { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 200; + } else { + time->tm_year = BCD2BIN(regs.year.bcd.value) + 100; + } + + /* tm_yday day of year [0,365] */ + time->tm_yday = 0; /* TODO */ + /* tm_isdst daylight savings flag */ + time->tm_isdst = 0; /* TODO */ + + return ret; +} + +int MAX31343::set_time(const struct tm *time) +{ + int ret; + rtc_time_regs_t regs; + + if (time == NULL) { + return -1; + } + + /********************************************************* + * +----------+------+---------------------------+-------+ + * | Member | Type | Meaning | Range | + * +----------+------+---------------------------+-------+ + * | tm_sec | int | seconds after the minute | 0-61* | + * | tm_min | int | minutes after the hour | 0-59 | + * | tm_hour | int | hours since midnight | 0-23 | + * | tm_mday | int | day of the month | 1-31 | + * | tm_mon | int | months since January | 0-11 | + * | tm_year | int | years since 1900 | | + * | tm_wday | int | days since Sunday | 0-6 | + * | tm_yday | int | days since January 1 | 0-365 | + * | tm_isdst | int | Daylight Saving Time flag | | + * +----------+------+---------------------------+-------+ + * * tm_sec is generally 0-59. The extra range is to accommodate for leap + * seconds in certain systems. + *********************************************************/ + regs.seconds.bcd.value = BIN2BCD(time->tm_sec); + regs.minutes.bcd.value = BIN2BCD(time->tm_min); + regs.hours.bcd.value= BIN2BCD(time->tm_hour); + regs.day.bcd.value = BIN2BCD(time->tm_wday + 1); + regs.date.bcd.value = BIN2BCD(time->tm_mday); + regs.month.bcd.value = BIN2BCD(time->tm_mon + 1); + + if (time->tm_year >= 200) { + regs.month.bits.century = 1; + regs.year.bcd.value = BIN2BCD(time->tm_year - 200); + } else if (time->tm_year >= 100) { + regs.month.bits.century = 0; + regs.year.bcd.value = BIN2BCD(time->tm_year - 100); + } else { + return -1; + } + + ret = write_register(MAX31343_R_SECONDS, (const uint8_t *) ®s, sizeof(regs)); + + return ret; +} + +int MAX31343::set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period) +{ + int ret; + regs_alarm_t regs; + + if (alarm_no == ALARM2) { + switch (period) { + case ALARM_PERIOD_EVERYSECOND: + case ALARM_PERIOD_ONETIME: + case ALARM_PERIOD_YEARLY: + return -1; // not support for alarm 2 + } + } + + + /* + * Set period + */ + + // clear all flag, default onetime + regs.sec.bits.a1m1 = 0; + regs.min.bits.a1m2 = 0; + regs.hrs.bits.a1m3 = 0; + regs.day_date.bits.a1m4 = 0; + regs.mon.bits.a1m5 = 0; + regs.mon.bits.a1m6 = 0; + regs.day_date.bits.dy_dt = 0; + + switch (period) { + case ALARM_PERIOD_ONETIME: + // do nothing + break; + case ALARM_PERIOD_YEARLY: + regs.mon.bits.a1m6 = 1; + break; + case ALARM_PERIOD_MONTHLY: + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + break; + case ALARM_PERIOD_WEEKLY: + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + regs.day_date.bits.dy_dt = 1; + break; + case ALARM_PERIOD_DAILY: + regs.day_date.bits.a1m4 = 1; + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + break; + case ALARM_PERIOD_HOURLY: + regs.hrs.bits.a1m3 = 1; + regs.day_date.bits.a1m4 = 1; + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + break; + case ALARM_PERIOD_EVERYMINUTE: + regs.min.bits.a1m2 = 1; + regs.hrs.bits.a1m3 = 1; + regs.day_date.bits.a1m4 = 1; + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + break; + case ALARM_PERIOD_EVERYSECOND: + regs.sec.bits.a1m1 = 1; + regs.min.bits.a1m2 = 1; + regs.hrs.bits.a1m3 = 1; + regs.day_date.bits.a1m4 = 1; + regs.mon.bits.a1m5 = 1; + regs.mon.bits.a1m6 = 1; + break; + default: + return -1; + } + + /* + * Convert time structure to alarm registers + */ + regs.sec.bcd.value = BIN2BCD(alarm_time->tm_sec); + regs.min.bcd.value = BIN2BCD(alarm_time->tm_min); + + regs.hrs.bcd.value = BIN2BCD(alarm_time->tm_hour); + + if (regs.day_date.bits.dy_dt == 0) { + /* Date match */ + regs.day_date.bcd_date.value = BIN2BCD(alarm_time->tm_mday); + } else { + /* Day match */ + regs.day_date.bcd_day.value = BIN2BCD(alarm_time->tm_wday); + } + regs.mon.bcd.value = BIN2BCD(alarm_time->tm_mon+1); + + if (alarm_time->tm_year >= 200) { + regs.year.bcd.value = BIN2BCD(alarm_time->tm_year - 200); + } else if (alarm_time->tm_year >= 100) { + regs.year.bcd.value = BIN2BCD(alarm_time->tm_year - 100); + } else { + return -1; + } + + + /* + * Write Registers + */ + uint8_t *ptr_regs = (uint8_t *)®s; + + if (alarm_no == ALARM1) { + ret = write_register(MAX31343_R_ALM1_SEC, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + /* XXX discard min, mon & sec registers */ + ret = write_register(MAX31343_R_ALM2_MIN, &ptr_regs[1], sizeof(regs_alarm_t)-3); + } + + return ret; +} + +int MAX31343::get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled) +{ + int ret; + regs_alarm_t regs; + uint8_t reg; + uint8_t *ptr_regs = (uint8_t *)®s; + + /* + * Read registers + */ + if (alarm_no == ALARM1) { + ret = read_register(MAX31343_R_ALM1_SEC, &ptr_regs[0], sizeof(regs_alarm_t)); + } else { + regs.sec.raw = 0; /* zeroise second register for alarm2 */ + /* XXX discard mon & sec registers */ + ret = read_register(MAX31343_R_ALM2_MIN, &ptr_regs[1], sizeof(regs_alarm_t)-2); + } + if (ret) { + return ret; + } + + + /* + * Convert alarm registers to time structure + */ + alarm_time->tm_sec = BCD2BIN(regs.sec.bcd.value); + alarm_time->tm_min = BCD2BIN(regs.min.bcd.value); + alarm_time->tm_hour = BCD2BIN(regs.hrs.bcd.value); + + if (regs.day_date.bits.dy_dt == 0) { /* date */ + alarm_time->tm_mday = BCD2BIN(regs.day_date.bcd_date.value); + } else { /* day */ + alarm_time->tm_wday = BCD2BIN(regs.day_date.bcd_day.value); + } + alarm_time->tm_mon = BCD2BIN(regs.mon.bcd.value) - 1; + alarm_time->tm_year = BCD2BIN(regs.year.bcd.value) + 100; /* XXX no century bit */ + + + /* + * Find period + */ + if(alarm_no == ALARM1) { + int alarm = regs.sec.bits.a1m1 | (regs.min.bits.a1m2<<1) + | (regs.hrs.bits.a1m3<<2) | (regs.day_date.bits.a1m4<<3) + | (regs.mon.bits.a1m5<<4) | (regs.mon.bits.a1m6<<5) + | (regs.day_date.bits.dy_dt<<6); + + switch(alarm) { + case 0b1111111: + case 0b0111111: + *period = ALARM_PERIOD_EVERYSECOND; + break; + case 0b1111110: + case 0b0111110: + *period = ALARM_PERIOD_EVERYMINUTE; + break; + case 0b1111100: + case 0b0111100: + *period = ALARM_PERIOD_HOURLY; + break; + case 0b0111000: + case 0b1111000: + *period = ALARM_PERIOD_DAILY; + break; + case 0b0110000: + *period = ALARM_PERIOD_MONTHLY; + break; + case 0b0100000: + *period = ALARM_PERIOD_YEARLY; + break; + case 0b0000000: + *period = ALARM_PERIOD_ONETIME; + break; + case 0b1110000: + *period = ALARM_PERIOD_WEEKLY; + } + } else { + int alarm = (regs.min.bits.a1m2) | (regs.hrs.bits.a1m3<<1) + | (regs.day_date.bits.a1m4<<2) + | (regs.day_date.bits.dy_dt<<3); + + switch(alarm) { + case 0b1111: + case 0b0111: + *period = ALARM_PERIOD_EVERYMINUTE; + break; + case 0b1110: + case 0b0110: + *period = ALARM_PERIOD_HOURLY; + break; + case 0b1100: + case 0b0100: + *period = ALARM_PERIOD_DAILY; + break; + case 0b0000: + *period = ALARM_PERIOD_MONTHLY; + break; + case 0b1000: + *period = ALARM_PERIOD_WEEKLY; + } + } + + /* + * Get enable status + */ + ret = read_register(MAX31343_R_INT_EN, (uint8_t *)®, 1); + if (ret) { + return ret; + } + + if (alarm_no == ALARM1) { + *is_enabled = (reg & (1 << INTR_ID_ALARM1)) != 0; + } else { + *is_enabled = (reg & (1 << INTR_ID_ALARM2)) != 0; + } + + return ret; +} + +int MAX31343::powerfail_threshold_level(comp_thresh_t th) +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_PWR_MGMT, ®, 1); + if (ret) { + return ret; + } + + reg &= ~MAX31343_F_PWR_MGMT_PFVT; + reg |= SET_BIT_VAL(th, MAX31343_F_PWR_MGMT_PFVT_POS, MAX31343_F_PWR_MGMT_PFVT); + + ret = write_register(MAX31343_R_PWR_MGMT, ®, 1); + + return ret; +} + +int MAX31343::supply_select(power_mgmt_supply_t supply) +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_PWR_MGMT, ®, 1); + if (ret) { + return ret; + } + + switch (supply) { + case POW_MGMT_SUPPLY_SEL_VCC: + reg |= MAX31343_F_PWR_MGMT_DMAN_SEL; + reg &= ~MAX31343_F_PWR_MGMT_D_VBACK_SEL; + break; + case POW_MGMT_SUPPLY_SEL_VBACK: + reg |= MAX31343_F_PWR_MGMT_DMAN_SEL; + reg |= MAX31343_F_PWR_MGMT_D_VBACK_SEL; + break; + case POW_MGMT_SUPPLY_SEL_AUTO: + default: + reg &= ~MAX31343_F_PWR_MGMT_DMAN_SEL; + break; + } + + ret = write_register(MAX31343_R_PWR_MGMT, ®, 1); + + return ret; +} + +int MAX31343::trickle_charger_enable(trickle_charger_ohm_t path) +{ + int ret; + uint8_t reg = 0; + + // 0x5 is trickle enable code + reg |= SET_BIT_VAL(5, MAX31343_F_TRICKLE_TCHE_POS, MAX31343_F_TRICKLE_TCHE); + reg |= SET_BIT_VAL(path, MAX31343_F_TRICKLE_D_TRICKLE_POS, MAX31343_F_TRICKLE_D_TRICKLE); + + ret = write_register(MAX31343_R_TRICKLE, (uint8_t *)®, 1); + + return ret; +} + +int MAX31343::trickle_charger_disable() +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_TRICKLE, ®, 1); + if (ret) { + return ret; + } + + reg &= ~MAX31343_F_TRICKLE_TCHE; + ret = write_register(MAX31343_R_TRICKLE, ®, 1); + + return ret; +} + +int MAX31343::set_square_wave_frequency(sqw_out_freq_t freq) +{ + int ret; + uint8_t cfg2; + + ret = read_register(MAX31343_R_CFG2, &cfg2, 1); + if (ret) { + return ret; + } + + cfg2 &= ~MAX31343_F_CFG2_SQW_HZ; + cfg2 |= SET_BIT_VAL(freq, MAX31343_F_CFG2_SQW_HZ_POS, MAX31343_F_CFG2_SQW_HZ); + + ret = write_register(MAX31343_R_CFG2, &cfg2, 1); + + return ret; +} + +int MAX31343::clko_enable(clko_freq_t freq) +{ + int ret; + uint8_t cfg2; + + ret = read_register(MAX31343_R_CFG2, (uint8_t *)&cfg2, 1); + if (ret) { + return ret; + } + + cfg2 |= MAX31343_F_CFG2_ENCLKO; + // + cfg2 &= ~MAX31343_F_CFG2_CLKO_HZ; + cfg2 |= SET_BIT_VAL(freq, MAX31343_F_CFG2_CLKO_HZ_POS, MAX31343_F_CFG2_CLKO_HZ); + + ret = write_register(MAX31343_R_CFG2, &cfg2, 1); + + return ret; +} + +int MAX31343::clko_disable() +{ + int ret; + uint8_t cfg2; + + ret = read_register(MAX31343_R_CFG2, &cfg2, 1); + if (ret) { + return ret; + } + + cfg2 &= ~MAX31343_F_CFG2_ENCLKO; + ret = write_register(MAX31343_R_CFG2, &cfg2, 1); + + return ret; +} + +int MAX31343::timer_init(uint8_t initial_value, bool repeat, timer_freq_t freq) +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + cfg &= ~MAX31343_F_TIMER_CONFIG_TE; /* timer is reset */ + cfg |= MAX31343_F_TIMER_CONFIG_TPAUSE; /* timer is paused */ + /* Timer repeat mode */ + if (repeat) { + cfg |= MAX31343_F_TIMER_CONFIG_TRPT; + } else { + cfg &= ~MAX31343_F_TIMER_CONFIG_TRPT; + } + cfg &= ~MAX31343_F_TIMER_CONFIG_TFS; + cfg |= SET_BIT_VAL(freq, MAX31343_F_TIMER_CONFIG_TFS_POS, MAX31343_F_TIMER_CONFIG_TFS); + + ret = write_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + ret = write_register(MAX31343_R_TIMER_INIT, &initial_value, 1); + + return ret; +} + +int MAX31343::timer_get(uint8_t &val) +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_TIMER_COUNT, (uint8_t *)®, 1); + if (ret) { + return ret; + } + val = reg; + + return ret; +} + +int MAX31343::timer_start() +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + cfg |= MAX31343_F_TIMER_CONFIG_TE; + cfg &= ~MAX31343_F_TIMER_CONFIG_TPAUSE; + + ret = write_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + + return ret; +} + +int MAX31343::timer_pause() +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + cfg |= MAX31343_F_TIMER_CONFIG_TE; + cfg |= MAX31343_F_TIMER_CONFIG_TPAUSE; + + ret = write_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + + return ret; +} + +int MAX31343::timer_continue() +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + cfg |= MAX31343_F_TIMER_CONFIG_TE; + cfg &= ~MAX31343_F_TIMER_CONFIG_TPAUSE; + + ret = write_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + + return ret; +} + +int MAX31343::timer_stop() +{ + int ret; + uint8_t cfg; + + ret = read_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + if (ret) { + return ret; + } + + cfg &= ~MAX31343_F_TIMER_CONFIG_TE; + cfg |= MAX31343_F_TIMER_CONFIG_TPAUSE; + + ret = write_register(MAX31343_R_TIMER_CONFIG, &cfg, 1); + + return ret; +} + +int MAX31343::set_data_retention_mode(bool enable) +{ + int ret; + uint8_t cfg1; + + ret = read_register(MAX31343_R_CFG1, &cfg1, 1); + if (ret) { + return ret; + } + + if (enable) { + cfg1 &= ~MAX31343_F_CFG1_ENOSC; + cfg1 |= MAX31343_F_CFG1_DATA_RET; + } else { + cfg1 |= MAX31343_F_CFG1_ENOSC; + cfg1 &= ~MAX31343_F_CFG1_DATA_RET; + } + + ret = write_register(MAX31343_R_CFG1, &cfg1, 1); + + return ret; +} + +int MAX31343::start_temp_conversion(bool automode/*=false*/, ttsint_t interval/*=TTS_INTERNAL_1SEC*/) +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_TS_CONFIG, ®, 1); + if (ret) { + return ret; + } + + if (automode) { + reg |= MAX31343_F_TS_CONFIG_AUTO_MODE; + // + reg &= ~MAX31343_F_TS_CONFIG_TTSINT; + reg |= SET_BIT_VAL(interval, MAX31343_F_TS_CONFIG_TTSINT_POS, MAX31343_F_TS_CONFIG_TTSINT); + } else { + reg &= ~MAX31343_F_TS_CONFIG_AUTO_MODE; + reg |= MAX31343_F_TS_CONFIG_ONESHOT_MODE; + } + + ret = write_register(MAX31343_R_TS_CONFIG, ®, 1); + + return ret; +} + +int MAX31343::is_temp_ready(void) +{ + int ret; + uint8_t reg; + + ret = read_register(MAX31343_R_TS_CONFIG, ®, 1); + if (ret) { + return ret; + } + + + if (reg & MAX31343_F_TS_CONFIG_ONESHOT_MODE) { + return MAX31343_ERR_BUSY; // means temperatrue NOT ready + } else { + return 0; // means temperature ready + } + + return ret; +} + +int MAX31343::get_temp(float &temp) +{ + int ret; + uint8_t buf[2]; + uint16_t count; + + #define TEMP_RESOLUTION_FOR_10_BIT (0.25f) + + ret = read_register(MAX31343_R_TEMP_MSB, buf, 2); + if (ret) { + return ret; + } + + // buf[0] includes upper 8 bits, buf[1](7:6 bits) includes lower 2 bits + count =(buf[0]<<2) | ( (buf[1]>>6) & 0x03 ); + + count &= 0x3FF; // Resolution is 10 bits, mask rest of it + + // convert count to temperature, 10th bit is sign bit + if (count & (1<<9) ) { + count = (count ^ 0x3FF) + 1; + temp = count * TEMP_RESOLUTION_FOR_10_BIT; + temp = 0 - temp; // convert to negative + } else { + temp = count * TEMP_RESOLUTION_FOR_10_BIT; + } + + return ret; +} + +int MAX31343::irq_enable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31343_R_INT_EN, &val8, 1); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 |= ALL_IRQ; + } else { + val8 |= id; + } + + ret = write_register(MAX31343_R_INT_EN, &val8, 1); + + return ret; +} + +int MAX31343::irq_disable(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + ret = read_register(MAX31343_R_INT_EN, &val8, 1); + if (ret) { + return ret; + } + + if (id == INTR_ID_ALL) { + val8 &= ~ALL_IRQ; + } else { + val8 &= ~id; + } + + ret = write_register(MAX31343_R_INT_EN, &val8, 1); + + return ret; +} + +int MAX31343::irq_clear_flag(intr_id_t id/*=INTR_ID_ALL*/) +{ + int ret; + uint8_t val8; + + // read status register to clear flags + ret = read_register(MAX31343_R_STATUS, &val8, 1); + + return ret; +} + +int MAX31343::sw_reset_assert() +{ + int ret; + uint8_t reg; + + reg = 1; /* Put device in reset state */ + ret = write_register(MAX31343_R_RTC_RESET, ®, 1); + + return ret; +} + +int MAX31343::sw_reset_release() +{ + int ret; + uint8_t reg; + + /* Remove device from reset state */ + reg = 0; + ret = write_register(MAX31343_R_RTC_RESET, ®, 1); + + return ret; +} + +int MAX31343::rtc_start() +{ + int ret; + uint8_t cfg1; + + ret = read_register(MAX31343_R_CFG1, &cfg1, 1); + if (ret) { + return ret; + } + + /* Enable the oscillator */ + cfg1 |= MAX31343_F_CFG1_ENOSC; + ret = write_register(MAX31343_R_CFG1, &cfg1, 1); + + return ret; +} + +int MAX31343::rtc_stop() +{ + int ret; + uint8_t cfg1; + + ret = read_register(MAX31343_R_CFG1, &cfg1, 1); + if (ret) { + return ret; + } + + /* Disable the oscillator */ + cfg1 &= ~MAX31343_F_CFG1_ENOSC; + + ret = write_register(MAX31343_R_CFG1, &cfg1, 1); + + return ret; +} + +int MAX31343::nvram_size() +{ + return (MAX31343_R_RAM_REG_END - MAX31343_R_RAM_REG_START) + 1; +} + +int MAX31343::nvram_write(int offset, const uint8_t *buffer, int length) +{ + int ret; + int totlen; + + totlen = (MAX31343_R_RAM_REG_END - MAX31343_R_RAM_REG_START) + 1; + + if ((offset + length) > totlen) { + return -1; + } + + if (length == 0) { + return 0; + } + + ret = write_register(MAX31343_R_RAM_REG_START + offset, buffer, length); + + return ret; +} + +int MAX31343::nvram_read(int offset, uint8_t *buffer, int length) +{ + int ret; + int totlen; + + totlen = (MAX31343_R_RAM_REG_END - MAX31343_R_RAM_REG_START) + 1; + + if ((offset + length) > totlen) { + return -1; + } + + if (length == 0) { + return 0; + } + + ret = read_register(MAX31343_R_RAM_REG_START + offset, buffer, length); + + return ret; +} diff --git a/src/MAX31343/MAX31343.h b/src/MAX31343/MAX31343.h new file mode 100644 index 0000000..7839b8c --- /dev/null +++ b/src/MAX31343/MAX31343.h @@ -0,0 +1,717 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef _MAX31343_H_ +#define _MAX31343_H_ + +#include + +#include +#include +#include + + +#define MAX31343_DRV_VERSION "v1.0.1" + +#define MAX31343_ERR_UNKNOWN (-1) +#define MAX31343_ERR_BUSY (-3) + + +class MAX31343 +{ + public: + /** + * @brief Uncompensated clock output frequency selection + */ + typedef enum { + CLKO_FREQ_1HZ, // 1Hz + CLKO_FREQ_2HZ, // 2Hz + CLKO_FREQ_4HZ, // 4Hz + CLKO_FREQ_8HZ, // 8Hz + CLKO_FREQ_16HZ, // 16Hz + CLKO_FREQ_32HZ, // 32Hz + CLKO_FREQ_64HZ, // 64Hz + CLKO_FREQ_128HZ, // 128Hz + CLKO_FREQ_32000HZ, // 32KHz + } clko_freq_t; + + /** + * @brief Square wave output frequency selection + */ + typedef enum { + SQW_OUT_FREQ_1HZ = 0, // 1Hz + SQW_OUT_FREQ_2HZ, // 2Hz + SQW_OUT_FREQ_4HZ, // 4Hz + SQW_OUT_FREQ_8HZ, // 8Hz + SQW_OUT_FREQ_16HZ, // 16Hz + SQW_OUT_FREQ_32HZ, // 32Hz + } sqw_out_freq_t; + + /** + * @brief Power fail threshold voltage + */ + typedef enum { + COMP_THRESH_1V85 = 1, // 1.85V + COMP_THRESH_2V15, // 2.15V + COMP_THRESH_2V40, // 2.40V + } comp_thresh_t; + + /** + * @brief Supply voltage select. + */ + typedef enum { + POW_MGMT_SUPPLY_SEL_AUTO, /**< Circuit decides whether to use VCC or VBACKUP */ + POW_MGMT_SUPPLY_SEL_VCC, /**< Use VCC as supply */ + POW_MGMT_SUPPLY_SEL_VBACK, /**< Use VBACKUP as supply */ + } power_mgmt_supply_t; + + /** + * @brief Selection of charging path's resistor value + */ + typedef enum { + TRICKLE_CHARGER_3K_S, /**< 3Kohm in series with a Schottky diode */ + TRICKLE_CHARGER_3K_2_S, /**< 3Kohm in series with a Schottky diode */ + TRICKLE_CHARGER_6K_S, /**< 6Kohm in series with a Schottky diode */ + TRICKLE_CHARGER_11K_S, /**< 11Kohm in series with a Schottky diode */ + TRICKLE_CHARGER_3K_D_S, /**< 3Kohm in series with a diode+Schottky diode */ + TRICKLE_CHARGER_3K_2_D_S, /**< 3Kohm in series with a diode+Schottky diode */ + TRICKLE_CHARGER_6K_D_S, /**< 6Kohm in series with a diode+Schottky diode */ + TRICKLE_CHARGER_11K_D_S, /**< 11Kohm in series with a diode+Schottky diode */ + TRICKLE_CHARGER_NO_CONNECTION, /**< No Connection */ + } trickle_charger_ohm_t; + + /** + * @brief Timer frequency selection + */ + typedef enum { + TIMER_FREQ_1024HZ, /**< 1024Hz */ + TIMER_FREQ_256HZ, /**< 256Hz */ + TIMER_FREQ_64HZ, /**< 64Hz */ + TIMER_FREQ_16HZ, /**< 16Hz */ + } timer_freq_t; + + /** + * @brief Selection of interrupt ids + */ + typedef enum { + INTR_ID_ALARM1 = 1<<0, // Alarm1 flag + INTR_ID_ALARM2 = 1<<1, // Alarm2 flag + INTR_ID_TIMER = 1<<2, // Timer interrupt flag + INTR_ID_TEMP = 1<<3, // Temp sense ready flag + INTR_ID_PFAIL = 1<<4, // Power fail interrupt enable + INTR_ID_DOSF = 1<<5, // Disable Oscillator flag + INTR_ID_ALL = 0xFF + } intr_id_t; + + /** + * @brief Alarm number selection + */ + typedef enum { + ALARM1, /**< Alarm number 1 */ + ALARM2, /**< Alarm number 2 */ + } alarm_no_t; + + /** + * @brief Alarm periodicity selection + */ + typedef enum { + ALARM_PERIOD_EVERYSECOND, /**< Once per second */ + ALARM_PERIOD_EVERYMINUTE, /**< Second match / Once per minute */ + ALARM_PERIOD_HOURLY, /**< Second and Minute match */ + ALARM_PERIOD_DAILY, /**< Hour, Minute and Second match*/ + ALARM_PERIOD_WEEKLY, /**< Day and Time match */ + ALARM_PERIOD_MONTHLY, /**< Date and Time match */ + ALARM_PERIOD_YEARLY, /**< Month, Date and Time match (Max31342 only) */ + ALARM_PERIOD_ONETIME, /**< Year, Month, Date and Time match (Max31342 only) */ + } alarm_period_t; + + /** + * @brief Selection of INTB/CLKOUT pin function + */ + typedef enum { + CONFIGURE_PIN_AS_CLKOUT, /**< Output is square wave */ + CONFIGURE_PIN_AS_INTB, /**< Output is interrupt */ + } config_intb_clkout_pin_t; + + /** + * @brief Temperature measurement interval for automatic mode + */ + typedef enum { + TTS_INTERNAL_1SEC = 0, + TTS_INTERNAL_2SEC, + TTS_INTERNAL_4SEC, + TTS_INTERNAL_8SEC, + TTS_INTERNAL_16SEC, + TTS_INTERNAL_32SEC, + TTS_INTERNAL_64SEC, + TTS_INTERNAL_128SEC, + } ttsint_t; + + typedef union { + uint8_t raw; + struct { + uint8_t a1f : 1; + uint8_t a2f : 1; + uint8_t tif : 1; + uint8_t tsf : 1; + uint8_t : 1; // not used + uint8_t pfail : 1; + uint8_t osf : 1; + uint8_t psdect : 1; + } bits; + } reg_status_t; + + typedef union { + uint16_t raw; + struct { + uint16_t : 1; // not used + uint16_t enosc : 1; + uint16_t : 1; // not used + uint16_t i2c_timeout : 1; + uint16_t data_ret : 1; + uint16_t : 3; // not used + uint16_t sqw_hz : 3; // sqw_out_freq_t + uint16_t clko_hz : 4; // clko_freq_t + uint16_t enclko : 1; + } bits; + } reg_cfg_t; + + MAX31343(TwoWire *i2c, uint8_t i2c_addr=MAX31343_I2C_ADDRESS); + + /** + * @brief First initialization, must be call before using class function + * + */ + void begin(void); + + /** + * @brief Read status byte + * + * @param[out] stat: Decoded status byte + * + * @returns 0 on success, negative error code on failure. + */ + int get_status(reg_status_t &stat); + + /** + * @brief Get configuration bytes + * + * @param[out] cfg: configuration values + * + * @returns 0 on success, negative error code on failure. + */ + int get_configuration(reg_cfg_t &cfg); + + /** + * @brief Set configuration bytes + * + * @param[in] cfg: configuration values + * + * @returns 0 on success, negative error code on failure. + */ + int set_configuration(reg_cfg_t cfg); + + /** + * @brief Read time info from RTC. + * + * @param[out] rtc_time Time info from RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int get_time(struct tm *rtc_ctime); + + /** + * @brief Set time info to RTC. + * + * @param[in] rtc_time Time info to be written to RTC. + * + * @returns 0 on success, negative error code on failure. + */ + int set_time(const struct tm *rtc_ctime); + + /** + * @brief Set an alarm condition + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[in] alarm_time Pointer to alarm time to be set + * @param[in] period Alarm periodicity, one of ALARM_PERIOD_* + * + * @return 0 on success, error code on failure + */ + int set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period); + + /** + * @brief Get alarm data & time + * + * @param[in] alarm_no Alarm number, ALARM1 or ALARM2 + * @param[out] alarm_time Pointer to alarm time to be filled in + * @param[out] period Pointer to the period of alarm, one of ALARM_PERIOD_* + * @param[out] is_enabled Pointer to the state of alarm + * + * @return 0 on success, error code on failure + */ + int get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled); + + /** + * @brief Set power fail threshold voltage + * + * @param[in] th Set Analog Comparator Threshold level, one of COMP_THRESH_* + * + * @return 0 on success, error code on failure + */ + int powerfail_threshold_level(comp_thresh_t th); + + /** + * @brief Select device power source + * + * @param[in] supply Supply selection, one of POW_MGMT_SUPPLY_SEL_* + * + * @return 0 on success, error code on failure + */ + int supply_select(power_mgmt_supply_t supply); + + /** + * @brief Configure trickle charger charging path, also enable it + * + * @param[in] path Value of resister + diodes + * + * @return 0 on success, error code on failure + */ + int trickle_charger_enable(trickle_charger_ohm_t path); + + /** + * @brief Disable trickle charger + * + * @return 0 on success, error code on failure + */ + int trickle_charger_disable(); + + /** + * @brief Select square wave output frequency selection + * + * @param[in] freq Clock frequency, one of SQUARE_WAVE_OUT_FREQ_* + * + * @return 0 on success, error code on failure + */ + int set_square_wave_frequency(sqw_out_freq_t freq); + + /** + * @brief Enable CLKO + * + * @param[in] freq Clock frequency, one of CLKO_FREQ_* + * + * @return 0 on success, error code on failure + */ + int clko_enable(clko_freq_t freq); + + /** + * @brief Disable CLKO + * + * @return 0 on success, error code on failure + */ + int clko_disable(); + + /** + * @brief Initialize timer + * + * @param[in] initial_value Timer initial value + * @param[in] repeat Timer repeat mode enable/disable + * @param[in] freq Timer frequency, one of TIMER_FREQ_* + * + * @return 0 on success, error code on failure + * + */ + int timer_init(uint8_t initial_value, bool repeat, timer_freq_t freq); + + /** + * @brief Read timer value + * + * @param[out] val: current timer count value + * + * @return 0 on success, error code on failure + */ + int timer_get(uint8_t &val); + + /** + * @brief Enable timer + * + * @return 0 on success, error code on failure + */ + int timer_start(); + + /** + * @brief Pause timer, timer value is preserved + * + * @return 0 on success, error code on failure + */ + int timer_pause(); + + /** + * @brief Start timer from the paused value + * + * @return 0 on success, error code on failure + */ + int timer_continue(); + + /** + * @brief Disable timer + * + * @return 0 on success, error code on failure + */ + int timer_stop(); + + /** + * @brief Put device into data retention mode + * + * @param[in] enable: true to enter data retain mode, + * false to exit from data retain mode + * + * @return 0 on success, error code on failure + */ + int set_data_retention_mode(bool enable); + + /** + * @brief Configure temperature sensor + * + * @param[in] automode temperature value + * @param[in] interval temperature read interval for automode, one of + * TTS_INTERNAL_* + * + * @return 0 on success, error code on failure + */ + int start_temp_conversion(bool automode=false, ttsint_t interval=TTS_INTERNAL_1SEC); + + /** + * @brief To decide temperature conversion finished or not + * Only valid for oneshot mode + * + * @return 0 on TempReady, MAX31343_ERR_BUSY on not ready, error code on failure + */ + int is_temp_ready(void); + + /** + * @brief Read Temperature + * + * @param[out] temp temperature value + * + * @return 0 on success, error code on failure + */ + int get_temp(float &temp); + + /** + * @brief Enable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_enable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Disable interrupt + * + * @param[in] id Interrupt id, one of INTR_ID_* + * + * @return 0 on success, error code on failure + */ + int irq_disable(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Clear the interrupt flag + * + * @return 0 on success, error code on failure + */ + int irq_clear_flag(intr_id_t id=INTR_ID_ALL); + + /** + * @brief Put device into reset state + * + * @return 0 on success, error code on failure + */ + int sw_reset_assert(); + + /** + * @brief Release device from state state + * + * @return 0 on success, error code on failure + */ + int sw_reset_release(); + + /** + * @brief Enable the RTC oscillator + * + * @return 0 on success, error code on failure + */ + int rtc_start(); + + /** + * @brief Disable the RTC oscillator + * + * @return 0 on success, error code on failure + */ + int rtc_stop(); + + /** + * @brief NVRAM size of the part + * + * @return 0 if part does not have a NVRAM, otherwise returns size + */ + int nvram_size(); + + /** + * @brief Non-volatile memory write + * + * @param[in] offset Offset of location in NVRAM + * @param[out] buffer Pointer to the data to be written + * @param[in] length Number of bytes to write + * + * @return 0 on success, error code on failure + */ + int nvram_write(int offset, const uint8_t *buffer, int length); + + /** + * @brief Non-volatile memory read + * + * @param[in] offset Offset of location in NVRAM + * @param[in] buffer Buffer to read in to + * @param[in] length Number of bytes to read + * + * @return 0 on success, error code on failure + */ + int nvram_read(int offset, uint8_t *buffer, int length); + + /** + * @brief Directly read value from register + * + * @param[in] reg: Register address + * @param[in] buf: destionation that values will be written + * @param[in] len: expected read len + * + * @return 0 on success, error code on failure + */ + int read_register(uint8_t reg, uint8_t *buf, uint8_t len=1); + + /** + * @brief Directly write value to register + * + * @param[in] reg: Register address + * @param[in] buf: values that would like to be written + * @param[in] len: buf len + * + * @return 0 on success, error code on failure + */ + int write_register(uint8_t reg, const uint8_t *buf, uint8_t len=1); + + private: + typedef struct { + union { + unsigned char raw; + struct { + unsigned char seconds : 4; /**< RTC seconds value. */ + unsigned char sec_10 : 3; /**< RTC seconds in multiples of 10 */ + unsigned char : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; + } seconds; + + union { + unsigned char raw; + struct { + unsigned char minutes : 4; /**< RTC minutes value */ + unsigned char min_10 : 3; /**< RTC minutes in multiples of 10 */ + unsigned char : 1; + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; + } minutes; + + union { + unsigned char raw; + struct { + unsigned char hour : 4; /**< RTC hours value */ + unsigned char hr_10 : 2; /**< RTC hours in multiples of 10 */ + unsigned char : 2; + } bits; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd; + } hours; + + union { + unsigned char raw; + struct { + unsigned char day : 3; /**< RTC days */ + unsigned char : 5; + } bits; + struct { + unsigned char value : 3; + unsigned char : 5; + } bcd; + } day; + + union { + unsigned char raw; + struct { + unsigned char date : 4; /**< RTC date */ + unsigned char date_10 : 2; /**< RTC date in multiples of 10 */ + unsigned char : 2; + } bits; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd; + } date; + + union { + unsigned char raw; + struct { + unsigned char month : 4; /**< RTC months */ + unsigned char month_10 : 1; /**< RTC month in multiples of 10 */ + unsigned char : 2; + unsigned char century : 1; /**< Century bit */ + } bits; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd; + } month; + + union { + unsigned char raw; + struct { + unsigned char year : 4; /**< RTC years */ + unsigned char year_10 : 4; /**< RTC year multiples of 10 */ + } bits; + struct { + unsigned char value : 8; + } bcd; + } year; + } rtc_time_regs_t; + + typedef struct { + union { + unsigned char raw; + struct { + unsigned char seconds : 4; /**< Alarm1 seconds */ + unsigned char sec_10 : 3; /**< Alarm1 seconds in multiples of 10 */ + unsigned char a1m1 : 1; /**< Alarm1 mask bit for minutes */ + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; + } sec; + + union { + unsigned char raw; + struct { + unsigned char minutes : 4; /**< Alarm1 minutes */ + unsigned char min_10 : 3; /**< Alarm1 minutes in multiples of 10 */ + unsigned char a1m2 : 1; /**< Alarm1 mask bit for minutes */ + } bits; + struct { + unsigned char value : 7; + unsigned char : 1; + } bcd; + } min; + + union { + unsigned char raw; + struct { + unsigned char hour : 4; /**< Alarm1 hours */ + unsigned char hr_10 : 2; /**< Alarm1 hours in multiples of 10 */ + unsigned char : 1; + unsigned char a1m3 : 1; /**< Alarm1 mask bit for hours */ + } bits; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd; + } hrs; + + union { + unsigned char raw; + struct { + unsigned char day_date : 4; /**< Alarm1 day/date */ + unsigned char date_10 : 2; /**< Alarm1 date in multiples of 10 */ + unsigned char dy_dt : 1; + unsigned char a1m4 : 1; /**< Alarm1 mask bit for day/date */ + } bits; + struct { + unsigned char value : 3; + unsigned char : 5; + } bcd_day; + struct { + unsigned char value : 6; + unsigned char : 2; + } bcd_date; + } day_date; + + union { + unsigned char raw; + struct { + unsigned char month : 4; /**< Alarm1 months */ + unsigned char month_10 : 1; /**< Alarm1 months in multiples of 10 */ + unsigned char : 1; + unsigned char a1m6 : 1; /**< Alarm1 mask bit for year */ + unsigned char a1m5 : 1; /**< Alarm1 mask bit for month */ + } bits; + struct { + unsigned char value : 5; + unsigned char : 3; + } bcd; + } mon; + + union { + unsigned char raw; + struct { + unsigned char year : 4; /* Alarm1 years */ + unsigned char year_10 : 4; /* Alarm1 multiples of 10 */ + } bits; + struct { + unsigned char value : 8; + } bcd; + } year; + } regs_alarm_t; + + TwoWire *m_i2c; + uint8_t m_slave_addr; + +}; + +#endif /* _MAX31343_H_ */ diff --git a/src/MAX31343/MAX31343_registers.h b/src/MAX31343/MAX31343_registers.h new file mode 100644 index 0000000..ab6c38f --- /dev/null +++ b/src/MAX31343/MAX31343_registers.h @@ -0,0 +1,152 @@ +/******************************************************************************* +* Copyright(C) Analog Devices Inc., All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files(the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* 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 ANALOG DEVICES 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. +* +* Except as contained in this notice, the name of Analog Devices Inc. +* shall not be used except as stated in the Analog Devices Inc. +* Branding Policy. +* +* The mere transfer of this software does not imply any licenses +* of trade secrets, proprietary technology, copyrights, patents, +* trademarks, maskwork rights, or any other form of intellectual +* property whatsoever. Analog Devices Inc.retains all ownership rights. +******************************************************************************** +*/ + +#ifndef _MAX31343_REGISTERS_H_ +#define _MAX31343_REGISTERS_H_ + +#define MAX31343_I2C_ADDRESS 0x68 + +#define MAX31343_R_STATUS ( 0x00 ) +#define MAX31343_R_INT_EN ( 0x01 ) +#define MAX31343_R_RTC_RESET ( 0x02 ) +#define MAX31343_R_CFG1 ( 0x03 ) +#define MAX31343_R_CFG2 ( 0x04 ) +#define MAX31343_R_TIMER_CONFIG ( 0x05 ) +#define MAX31343_R_SECONDS ( 0x06 ) +#define MAX31343_R_MINUTES ( 0x07 ) +#define MAX31343_R_HOURS ( 0x08 ) +#define MAX31343_R_DAY ( 0x09 ) +#define MAX31343_R_DATE ( 0x0A ) +#define MAX31343_R_MONTH ( 0x0B ) +#define MAX31343_R_YEAR ( 0x0C ) +#define MAX31343_R_ALM1_SEC ( 0x0D ) +#define MAX31343_R_ALM1_MIN ( 0x0E ) +#define MAX31343_R_ALM1_HRS ( 0x0F ) +#define MAX31343_R_ALM1DAY_DATE ( 0x10 ) +#define MAX31343_R_ALM1_MON ( 0x11 ) +#define MAX31343_R_ALM1_YEAR ( 0x12 ) +#define MAX31343_R_ALM2_MIN ( 0x13 ) +#define MAX31343_R_ALM2_HRS ( 0x14 ) +#define MAX31343_R_ALM2DAY_DATE ( 0x15 ) +#define MAX31343_R_TIMER_COUNT ( 0x16 ) +#define MAX31343_R_TIMER_INIT ( 0x17 ) +#define MAX31343_R_PWR_MGMT ( 0x18 ) +#define MAX31343_R_TRICKLE ( 0x19 ) +#define MAX31343_R_TEMP_MSB ( 0x1A ) +#define MAX31343_R_TEMP_LSB ( 0x1B ) +#define MAX31343_R_TS_CONFIG ( 0x1C ) +#define MAX31343_R_RAM_REG_START ( 0x22 ) +#define MAX31343_R_RAM_REG_END ( 0x61 ) + +//status register bits +#define MAX31343_F_STATUS_A1F_POS 0 +#define MAX31343_F_STATUS_A1F (1<