Demo the function of RTC module AM1805
Dependencies: mbed-dev
Fork of I2C_HelloWorld_Mbed by
Diff: AM1805.cpp
- Revision:
- 1:c45df8c46fa8
- Child:
- 2:16b8f527b5c7
diff -r f76c26307f9a -r c45df8c46fa8 AM1805.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/AM1805.cpp Thu Dec 24 03:30:20 2015 +0000
@@ -0,0 +1,641 @@
+/* AM1805 Sample code: external RTC module is used by host MCU */
+
+#include "mbed.h"
+#include "AM1805.h"
+
+#define I2C_SDA p28
+#define I2C_SCL p27
+#define AM1805_I2C_ADDRESS 0xD2
+/* Register Map */
+#define AM1805_REG_HUNDREDTHS 0x00
+#define AM1805_REG_ALM_HUN 0x08
+#define AM1805_REG_STATUS 0x0F
+#define AM1805_REG_CTRL1 0x10
+#define AM1805_REG_CTRL2 0x11
+#define AM1805_REG_INTMASK 0x12
+#define AM1805_REG_SLEEPCTRL 0x17
+#define AM1805_REG_TIM_CTRL 0x18
+#define AM1805_REG_CDTIM 0x19
+#define AM1805_REG_TIMINIT 0x1A
+#define AM1805_REG_WDT 0x1B
+#define AM1805_REG_OSCSTATUS 0x1D
+#define AM1805_REG_ID0 0x28
+#define AM1805_REG_EXTADDR_REG 0x3F
+/* Register Value */
+#define AM1805_VALUE_ID0 0x18
+
+#define AM1805_DEBUG
+
+I2C i2c(I2C_SDA, I2C_SCL);
+#ifdef AM1805_DEBUG
+Serial pc(p13,p14);
+#endif
+
+static uint8_t get_extension_address(uint8_t address)
+{
+ uint8_t xadd;
+ char temp;
+
+ am1805_register_read(AM1805_REG_EXTADDR_REG, &temp, 1);
+ temp = temp & 0xC0;
+
+ if (address < 64) { xadd = 0x8; }
+ else if (address < 128) { xadd = 0x9; }
+ else if (address < 192) { xadd = 0xA; }
+ else { xadd = 0xB; }
+ return (xadd | temp);
+}
+
+/* Set one or more bits in the selected register, selected by 1's in the mask */
+static void setreg(char address, uint8_t mask)
+{
+ char temp;
+
+ am1805_register_read(address, &temp, 1);
+ temp |= mask;
+ am1805_register_write(address, temp);
+}
+
+/* Clear one or more bits in the selected register, selected by 1's in the mask */
+static void clrreg(char address, uint8_t mask)
+{
+ char temp;
+
+ am1805_register_read(address, &temp, 1);
+ temp &= ~mask;
+ am1805_register_write(address, temp);
+}
+
+static bool am1805_verify_product_id(void)
+{
+ char who_am_i[1];
+ am1805_register_read(AM1805_REG_ID0, &who_am_i[0], 1);
+#ifdef AM1805_DEBUG
+ pc.printf("ID:%x\r\n",who_am_i[0]);
+#endif
+ if (who_am_i[0] != AM1805_VALUE_ID0)
+ return false;
+ else
+ return true;
+}
+
+bool am1805_init(void)
+{
+ bool transfer_succeeded = false;
+
+ i2c.frequency(400000);
+
+ /* Read and verify product ID */
+ transfer_succeeded = am1805_verify_product_id();
+
+ return transfer_succeeded;
+}
+
+void am1805_register_read(char register_address, char *destination, uint8_t number_of_bytes)
+{
+ i2c.write(AM1805_I2C_ADDRESS, ®ister_address, 1, 1);
+ i2c.read(AM1805_I2C_ADDRESS, destination, number_of_bytes);
+}
+
+void am1805_register_write(char register_address, uint8_t value)
+{
+ char tx_data[2];
+
+ tx_data[0] = register_address;
+ tx_data[1] = value;
+ i2c.write(AM1805_I2C_ADDRESS, tx_data, 2);
+
+}
+
+void am1805_burst_write(uint8_t *tx_data, uint8_t number_of_bytes)
+{
+ i2c.write(AM1805_I2C_ADDRESS, (char *)tx_data, number_of_bytes);
+}
+
+void am1805_config_input_interrupt(input_interrupt_t index_Interrupt)
+{
+ switch(index_Interrupt) {
+ case XT1_INTERRUPT:
+ /* Set input interrupt pin EX1T */
+ clrreg(AM1805_REG_STATUS,0x01); // Clear EX1
+ setreg(AM1805_REG_INTMASK,0x01); // Set EX1E
+ break;
+ case XT2_INTERRUPT:
+ /* Set input interrupt pin WDI */
+ clrreg(AM1805_REG_STATUS,0x02); // Clear EX2
+ setreg(AM1805_REG_INTMASK,0x02); // Set EX2E
+ break;
+ default:
+#ifdef AM1805_DEBUG
+ pc.printf("Wrong Input Interrupt Index\r\n");
+#endif
+ break;
+ }
+}
+
+
+// Read a byte from local RAM
+uint8_t am1805_read_ram(uint8_t address)
+{
+ uint8_t xadd;
+ char temp;
+ uint8_t reg_ram = 0;
+
+ xadd = get_extension_address(address); // Calc XADDR value from address
+ am1805_register_write(AM1805_REG_EXTADDR_REG, xadd); // Load the XADDR register
+ reg_ram = (address & 0x3F) | 0x40; // Read the data
+ am1805_register_read(reg_ram, &temp, 1);
+#ifdef AM1805_DEBUG
+ pc.printf("Read from addr:%x Data:%x\r\n",address,temp);
+#endif
+ return (uint8_t)temp;
+}
+
+// Write a byte to local RAM
+void am1805_write_ram(uint8_t address, uint8_t data)
+{
+ uint8_t xadd;
+ uint8_t reg_ram = 0;
+
+ xadd = get_extension_address(address); // Calc XADDR value from address
+ am1805_register_write(AM1805_REG_EXTADDR_REG, xadd); // Load the XADDR register
+ reg_ram = (address & 0x3F) | 0x40;
+ am1805_register_write(reg_ram, data); // Write the data
+}
+
+/*
+ * hundredth : 0 ~ 99
+ * second : 0 ~ 59
+ * minute : 0 ~ 59
+ * weekday : 0 ~ 6
+ * month : 1 ~ 12
+ * year : 0 ~ 99
+ * mode : 0 ~ 2
+ */
+void am1805_set_time(time_reg_struct_t time_regs)
+{
+ char temp;
+ uint8_t temp_buff[9];
+
+ /*
+ * Determine whether 12 or 24-hour timekeeping mode is being used
+ * and set the 1224 bit appropriately
+ */
+ if (time_regs.mode == 2) // 24-hour day
+ {
+ clrreg(AM1805_REG_CTRL1, 0x40);
+ }
+ else if (time_regs.mode == 1) // 12-hour day PM
+ {
+ time_regs.hour |= 0x20; // Set AM/PM
+ setreg(AM1805_REG_CTRL1, 0x40);
+ }
+ else // 12-hour day AM
+ {
+ setreg(AM1805_REG_CTRL1, 0x40);
+ }
+
+ /* Set the WRTC bit to enable counter writes. */
+ setreg(AM1805_REG_CTRL1, 0x01);
+
+ /* Set the correct century */
+ if (time_regs.century == 0)
+ {
+ clrreg(AM1805_REG_STATUS, 0x80);
+ }
+ else
+ {
+ setreg(AM1805_REG_STATUS, 0x80);
+ }
+
+ /* Write all of the time counters */
+ temp_buff[0] = AM1805_REG_HUNDREDTHS;
+ temp_buff[1] = time_regs.hundredth;
+ temp_buff[2] = time_regs.second;
+ temp_buff[3] = time_regs.minute;
+ temp_buff[4] = time_regs.hour;
+ temp_buff[5] = time_regs.date;
+ temp_buff[6] = time_regs.month;
+ temp_buff[7] = time_regs.year;
+ temp_buff[8] = time_regs.weekday;
+
+ /* Write the values to the AM18XX */
+ am1805_burst_write(temp_buff, sizeof(temp_buff));
+
+ /* Load the final value of the WRTC bit based on the value of protect */
+ am1805_register_read(AM1805_REG_CTRL1, &temp, 1);
+ temp &= 0x7E; // Clear the WRTC bit and the STOP bit
+ //temp_buff[1] |= (0x01 & (~protect)); // Invert the protect bit and update WRTC
+ am1805_register_write(AM1805_REG_CTRL1, temp);
+
+ return;
+}
+
+void am1805_get_time(time_reg_struct_t *time_regs)
+{
+ char temp_buff[8];
+ char time_mode;
+
+ /* Read the counters. */
+ am1805_register_read(AM1805_REG_HUNDREDTHS, temp_buff, 8);
+
+ time_regs->hundredth = temp_buff[0];
+ time_regs->second = temp_buff[1];
+ time_regs->minute = temp_buff[2];
+ time_regs->hour = temp_buff[3];
+ time_regs->date = temp_buff[4];
+ time_regs->month = temp_buff[5];
+ time_regs->year = temp_buff[6];
+ time_regs->weekday = temp_buff[7];
+
+ /* Get the current hours format mode 12:24 */
+ am1805_register_read(AM1805_REG_CTRL1, &time_mode, 1);
+ if ((time_mode & 0x40) == 0)
+ {
+ /* 24-hour mode. */
+ time_regs->mode = 2;
+ time_regs->hour = time_regs->hour & 0x3F; // Get tens:ones
+ }
+ else
+ {
+ /* 12-hour mode. Get PM:AM. */
+ time_regs->mode = (time_regs->hour & 0x20) ? 1 : 0; // PM : AM
+ time_regs->hour &= 0x1F; // Get tens:ones
+ }
+
+ time_regs->hundredth = temp_buff[0];
+ time_regs->second = temp_buff[1];
+ time_regs->minute = temp_buff[2];
+ time_regs->hour = temp_buff[3];
+ time_regs->date = temp_buff[4];
+ time_regs->month = temp_buff[5];
+ time_regs->year = temp_buff[6];
+ time_regs->weekday = temp_buff[7];
+
+#ifdef AM1805_DEBUG
+ pc.printf("hundredth:%x\r\n",time_regs->hundredth);
+ pc.printf("second:%x\r\n",time_regs->second);
+ pc.printf("minute:%x\r\n",time_regs->minute);
+ pc.printf("hour:%x\r\n",time_regs->hour);
+ pc.printf("date:%x\r\n",time_regs->date);
+ pc.printf("month:%x\r\n",time_regs->month);
+ pc.printf("year:%x\r\n",time_regs->year);
+ pc.printf("weekday:%x\r\n",time_regs->weekday);
+#endif
+}
+
+void am1805_config_alarm(time_reg_struct_t time_regs, alarm_repeat_t repeat, interrupt_mode_t intmode, interrupt_pin_t pin)
+{
+ char temp;
+ uint8_t temp_buff[9];
+
+ /* Determine whether a 12-hour or a 24-hour time keeping mode is being used */
+ if (time_regs.mode == 1)
+ {
+ /* A 12-hour day PM */
+ time_regs.hour = time_regs.hour | 0x20; // Set AM/PM
+ }
+
+ /* Write all of the time counters */
+ temp_buff[0] = AM1805_REG_ALM_HUN;
+ temp_buff[1] = time_regs.hundredth;
+ temp_buff[2] = time_regs.second;
+ temp_buff[3] = time_regs.minute;
+ temp_buff[4] = time_regs.hour;
+ temp_buff[5] = time_regs.date;
+ temp_buff[6] = time_regs.month;
+ temp_buff[7] = time_regs.weekday;
+
+ clrreg(AM1805_REG_TIM_CTRL, 0x1C); // Clear the RPT field
+ clrreg(AM1805_REG_INTMASK, 0x64); // Clear the AIE bit and IM field
+ clrreg(AM1805_REG_STATUS, 0x04); // Clear the ALM flag
+//////////////////////////////
+clrreg(AM1805_REG_CTRL1, 0x10);
+/////////////////////////////
+
+ if (pin == PIN_FOUT_nIRQ)
+ {
+ /* Interrupt on FOUT/nIRQ */
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get the Control2 Register
+/////////////////
+//pc.printf("111111111:AM1805_REG_CTRL2:%x\r\n",temp);
+setreg(AM1805_REG_CTRL2, 0x03); // Set OUT1S to 3
+/////////////////
+ temp = (temp & 0x03); // Extract the OUT1S field
+ if (temp != 0) // Not already selecting nIRQ
+ {
+ setreg(AM1805_REG_CTRL2, 0x03); // Set OUT1S to 3
+///////////////////
+//pc.printf("2222222:AM1805_REG_CTRL2:%x\r\n",temp);
+///////////////////
+ }
+///////////////
+char bufff = 0;
+am1805_register_read(AM1805_REG_CTRL1, &bufff, 1); // Get the Control2 Register
+//pc.printf("99999:AM1805_REG_CTRL1:%x\r\n",bufff);
+am1805_register_read(AM1805_REG_CTRL2, &bufff, 1); // Get the Control2 Register
+//pc.printf("33333:AM1805_REG_CTRL2:%x\r\n",bufff);
+am1805_register_read(AM1805_REG_STATUS, &bufff, 1); // Get the Control2 Register
+//pc.printf("55555:AM1805_REG_STATUS:%x\r\n",bufff);
+///////////////
+ }
+ if (pin == PIN_PSW_nIRQ2)
+ {
+ /* Interrupt on PSW/nIRQ2 */
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get the Control2 Register
+ temp &= 0x1C; // Extract the OUT2S field
+ if (temp != 0) // Not already selecting nIRQ
+ {
+ clrreg(AM1805_REG_CTRL2, 0x1C); // Clear OUT2S
+ setreg(AM1805_REG_CTRL2, 0x0C); // Set OUT2S to 3
+ }
+ }
+
+ if (repeat == ONCE_PER_10TH_SEC)
+ {
+ /* 10ths interrupt */
+ temp_buff[1] |= 0xF0;
+ repeat = ONCE_PER_SECOND; // Select correct RPT value
+ }
+ if (repeat == ONCE_PER_100TH_SEC)
+ {
+ /* 100ths interrupt */
+ temp_buff[1] = 0xFF;
+ repeat = ONCE_PER_SECOND; // Select correct RPT value
+ }
+ if (repeat != 0) // Don't initiate if repeat = 0
+ {
+ temp = (repeat << 2); // Set the RPT field to the value of repeat
+ setreg(AM1805_REG_TIM_CTRL, temp); // Was previously cleared
+ //setreg(AM1805_REG_INTMASK, (intmode << 5)); // Set the alarm interrupt mode
+ setreg(AM1805_REG_INTMASK, 0x60); // Set the alarm interrupt mode
+ am1805_burst_write(temp_buff, 8); // Execute the burst write
+ setreg(AM1805_REG_INTMASK, 0x04); // Set the AIE bit
+///////////////////
+char bufff = 0;
+am1805_register_read(AM1805_REG_INTMASK, &bufff, 1); // Get the Control2 Register
+//pc.printf("6666:AM1805_REG_INTMASK:%x\r\n",bufff);
+am1805_register_read(AM1805_REG_TIM_CTRL, &bufff, 1); // Get the Control2 Register
+//pc.printf("7777:AM1805_REG_TIM_CTRL:%x\r\n",bufff);
+am1805_register_read(AM1805_REG_STATUS, &bufff, 1); // Get the Control2 Register
+//pc.printf("8888:AM1805_REG_STATUS:%x\r\n",bufff);
+/* Read the counters. */
+char buff[7];
+am1805_register_read(AM1805_REG_ALM_HUN, buff, 7);
+uint8_t i;
+for(i = 0; i < 7 ;i++){}
+//pc.printf("buff[%d]:%x\r\n",i,buff[i]);
+///////////////
+ }
+ else
+ setreg(AM1805_REG_INTMASK, 0x60); // Set IM field to 0x3 (reset value) to minimize current draw
+
+ return;
+}
+
+void am1805_config_countdown_timer(count_down_range_t range, int32_t period, count_down_repeat_t repeat, interrupt_pin_t pin)
+{
+ uint8_t tm;
+ uint8_t trpt;
+ uint8_t tfs;
+ uint8_t te;
+ char temp;
+ char tctrl;
+ int32_t timer;
+ char oscmode;
+
+ /* 0 = XT, 1 = RC */
+ am1805_register_read(AM1805_REG_OSCSTATUS, &oscmode, 1);
+ oscmode = (oscmode & 0x10) ? 1 : 0;
+
+ /* disable count down timer */
+ if (pin == INTERNAL_FLAG)
+ {
+ te = 0;
+ }
+ else
+ {
+ te = 1;
+ if (repeat == SINGLE_LEVEL_INTERRUPT)
+ {
+ /* Level interrupt */
+ tm = 1; // Level
+ trpt = 0; // No repeat
+ if (range == PERIOD_US)
+ {
+ /* Microseconds */
+ if (oscmode == 0)
+ {
+ /* XT Mode */
+ if (period <= 62500) // Use 4K Hz
+ {
+ tfs = 0;
+ timer = (period * 4096);
+ timer = timer / 1000000;
+ timer = timer - 1;
+ }
+ else if (period <= 16384000) // Use 64 Hz
+ {
+ tfs = 1;
+ timer = (period * 64);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ else // Use 1 Hz
+ {
+ tfs = 2;
+ timer = period / 1000000;
+ timer = timer - 1;
+ }
+ }
+ else
+ {
+ /* RC Mode */
+ if (period <= 2000000) { // Use 128 Hz
+ tfs = 0;
+ timer = (period * 128);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ else if (period <= 4000000) { // Use 64 Hz
+ tfs = 1;
+ timer = (period * 64);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ else { // Use 1 Hz
+ tfs = 2;
+ timer = period / 1000000;
+ timer = timer - 1;
+ }
+ }
+ }
+ else
+ {
+ /* Seconds */
+ if (period <= 256)
+ {
+ /* Use 1 Hz */
+ tfs = 2;
+ timer = period - 1;
+ }
+ else
+ {
+ /* Use 1/60 Hz */
+ tfs = 3;
+ timer = period / 60;
+ timer = timer - 1;
+ }
+ }
+ }
+ else
+ {
+ /* Pulse interrupts */
+ tm = 0; // Pulse
+ trpt = repeat & 0x01; // Set up repeat
+ if (repeat < REPEAT_PLUSE_1_128_SEC)
+ {
+ tfs = 0;
+ if (oscmode == 0)
+ {
+ timer = (period * 4096);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ else
+ {
+ timer = (period * 128);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ }
+ else if (repeat < REPEAT_PLUSE_1_64_SEC)
+ {
+ tfs = 1;
+ timer = (period * 128);
+ timer /= 1000000;
+ timer = timer - 1;
+ }
+ else if (period <= 256)
+ {
+ /* Use 1 Hz */
+ tfs = 2;
+ timer = period - 1;
+ }
+ else
+ {
+ /* Use 1/60 Hz */
+ tfs = 3;
+ timer = period / 60;
+ timer = timer - 1;
+ }
+ }
+ }
+
+ am1805_register_read(AM1805_REG_TIM_CTRL, &tctrl, 1); // Get TCTRL, keep RPT, clear TE
+ tctrl = tctrl & 0x1C;
+ am1805_register_write(AM1805_REG_TIM_CTRL, tctrl);
+
+ tctrl = tctrl | (te * 0x80) | (tm * 0x40) | (trpt * 0x20) | tfs; // Merge the fields
+
+ if (pin == PIN_FOUT_nIRQ) // generate nTIRQ interrupt on FOUT/nIRQ (asserted low)
+ {
+ clrreg(AM1805_REG_CTRL2, 0x3); // Clear OUT1S
+ }
+ if (pin == PIN_PSW_nIRQ2) // generate nTIRQ interrupt on PSW/nIRQ2 (asserted low)
+ {
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get OUT2S
+ if ((temp & 0x1C) != 0)
+ {
+ temp = (temp & 0xE3) | 0x14; // If OUT2S != 0, set OUT2S to 5
+ }
+ am1805_register_write(AM1805_REG_CTRL2, temp); // Write back
+ }
+ if (pin != 0)
+ {
+ clrreg(AM1805_REG_STATUS,0x08); // Clear TIM
+ setreg(AM1805_REG_INTMASK,0x08); // Set TIE
+ am1805_register_write(AM1805_REG_CDTIM, timer); // Initialize the timer
+ am1805_register_write(AM1805_REG_TIMINIT, timer); // Initialize the timer repeat
+ am1805_register_write(AM1805_REG_TIM_CTRL, tctrl); // Start the timer
+ }
+
+ return ;
+}
+
+/** Parameter:
+ * timeout - minimum timeout period in 7.8 ms periods (0 to 7)
+ * mode - sleep mode (nRST modes not available in AM08xx)
+ * 0 => nRST is pulled low in sleep mode
+ * 1 => PSW/nIRQ2 is pulled high on a sleep
+ * 2 => nRST pulled low and PSW/nIRQ2 pulled high on sleep
+ * error ?returned value of the attempted sleep command
+ * 0 => sleep request accepted, sleep mode will be initiated in timeout seconds
+ * 1 => illegal input values
+ * 2 => sleep request declined, interrupt is currently pending
+ * 3 => sleep request declined, no sleep trigger interrupt enabled
+**/
+void am1805_set_sleep(uint8_t timeout, uint8_t mode)
+{
+ uint8_t slres = 0;
+ char temp = 0;
+
+#ifdef AM1805_DEBUG
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLST bit (temp & 0x08)
+
+ if ( ( temp & 0x08 ) == 0)
+ {
+ pc.printf("Previous Sleep Failed\r\n");
+ } else {
+ pc.printf("Previous Sleep Successful\r\n");
+ }
+ clrreg(AM1805_REG_CTRL2,0x08); // Clear SLST
+
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLST bit (temp & 0x08)
+
+ if ( ( temp & 0x08 ) == 0)
+ {
+ pc.printf("Clear Succ\r\n");
+ } else {
+ pc.printf("Clear Fail\r\n");
+ }
+ clrreg(AM1805_REG_CTRL2,0x08); // Clear SLST
+#endif
+
+ if (mode > 0)
+ {
+ /* Sleep to PSW/nIRQ2 */
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Read OUT2S
+ temp = (temp & 0xE3) | 0x18; // MUST NOT WRITE OUT2S WITH 000
+ am1805_register_write(AM1805_REG_CTRL2, temp); // Write value to OUT2S
+ slres = 0;
+ }
+
+ temp = timeout | (slres << 6) | 0x80; // Assemble SLEEP register value
+ am1805_register_write(AM1805_REG_SLEEPCTRL, temp); // Write to the register
+
+#ifdef AM1805_DEBUG
+ /* Determine if SLEEP was accepted */
+ am1805_register_read(AM1805_REG_CTRL2, &temp, 1); // Get SLP bit (temp & 0x80)
+
+ if ( ( temp & 0x80 ) == 0)
+ {
+ char reg_wdi_value = 0;
+ /* SLEEP did not happen - determine why and return reason. */
+ am1805_register_read(AM1805_REG_INTMASK, &temp, 1); // Get status register interrupt enables
+ am1805_register_read(AM1805_REG_WDT, ®_wdi_value, 1); // Get WDT register
+ if ((( temp & 0x0F ) == 0) & (((reg_wdi_value & 0x7C) == 0) || ((reg_wdi_value & 0x80) == 0x80)))
+ {
+ pc.printf("No trigger interrupts enabled\r\n");
+ }
+ else
+ {
+ pc.printf("Interrupt pending\r\n");
+ }
+ }
+ else
+ {
+ pc.printf("SLEEP request successful\r\n");
+ }
+#endif
+}

AM1805 Real-Time Clock with Power Management